├── client
├── public
│ ├── robots.txt
│ ├── favicon.ico
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── index.html
├── src
│ ├── utils
│ │ ├── Images
│ │ │ ├── Header.png
│ │ │ ├── Logo.png
│ │ │ └── AuthImage.png
│ │ ├── Themes.js
│ │ └── data.js
│ ├── pages
│ │ ├── NewArrival.jsx
│ │ ├── Favourite.jsx
│ │ ├── Home.jsx
│ │ ├── Authentication.jsx
│ │ ├── ShopListing.jsx
│ │ ├── ProductDetails.jsx
│ │ └── Cart.jsx
│ ├── index.js
│ ├── index.css
│ ├── redux
│ │ ├── reducers
│ │ │ ├── snackbarSlice.js
│ │ │ └── userSlice.js
│ │ └── store.js
│ ├── components
│ │ ├── ToastMessage.jsx
│ │ ├── cards
│ │ │ ├── ProductCategoryCard.jsx
│ │ │ └── ProductCard.jsx
│ │ ├── Button.jsx
│ │ ├── SignIn.jsx
│ │ ├── SignUp.jsx
│ │ ├── TextInput.jsx
│ │ └── Navbar.jsx
│ ├── api
│ │ └── index.js
│ └── App.js
├── package.json
└── README.md
├── server
├── error.js
├── routes
│ ├── Products.js
│ └── User.js
├── package.json
├── middleware
│ └── verifyToken.js
├── models
│ ├── Orders.js
│ ├── Products.js
│ └── User.js
├── index.js
├── controllers
│ ├── Products.js
│ └── User.js
└── package-lock.json
└── .gitignore
/client/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/client/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/public/favicon.ico
--------------------------------------------------------------------------------
/client/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/public/logo192.png
--------------------------------------------------------------------------------
/client/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/public/logo512.png
--------------------------------------------------------------------------------
/client/src/utils/Images/Header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/src/utils/Images/Header.png
--------------------------------------------------------------------------------
/client/src/utils/Images/Logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/src/utils/Images/Logo.png
--------------------------------------------------------------------------------
/client/src/utils/Images/AuthImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/rishavchanda/Ecomerce-website/HEAD/client/src/utils/Images/AuthImage.png
--------------------------------------------------------------------------------
/client/src/pages/NewArrival.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | const NewArrival = () => {
4 | return (
5 |
NewArrival
6 | )
7 | }
8 |
9 | export default NewArrival
--------------------------------------------------------------------------------
/server/error.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line import/prefer-default-export
2 | export const createError = (status, message) => {
3 | const err = new Error();
4 | err.status = status;
5 | err.message = message;
6 | return err;
7 | };
8 |
--------------------------------------------------------------------------------
/server/routes/Products.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import {
3 | addProducts,
4 | getProductById,
5 | getproducts,
6 | } from "../controllers/Products.js";
7 |
8 | const router = express.Router();
9 |
10 | router.post("/add", addProducts);
11 | router.get("/", getproducts);
12 | router.get("/:id", getProductById);
13 |
14 | export default router;
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 |
5 | /.pnp
6 | .pnp.js
7 |
8 | /client/node_modules
9 | /server/node_modules
10 | /server/.env
11 |
12 | # testing
13 | /coverage
14 |
15 | # production
16 | /build
17 |
18 | # misc
19 | .DS_Store
20 | .env.local
21 | .env.development.local
22 | .env.test.local
23 | .env.production.local
24 |
25 | npm-debug.log*
26 | yarn-debug.log*
27 | yarn-error.log*
28 |
--------------------------------------------------------------------------------
/server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "server",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "type": "module",
7 | "scripts": {
8 | "start": "nodemon index.js",
9 | "test": "echo \"Error: no test specified\" && exit 1"
10 | },
11 | "author": "",
12 | "license": "ISC",
13 | "dependencies": {
14 | "bcrypt": "^5.1.1",
15 | "cors": "^2.8.5",
16 | "dotenv": "^16.4.5",
17 | "express": "^4.18.3",
18 | "jsonwebtoken": "^9.0.2",
19 | "mongoose": "^8.2.2",
20 | "nodemon": "^3.1.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/client/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import "./index.css";
4 | import App from "./App";
5 | import { Provider } from "react-redux";
6 | import { PersistGate } from "redux-persist/integration/react";
7 | import { store, persistor } from "./redux/store";
8 |
9 | const root = ReactDOM.createRoot(document.getElementById("root"));
10 | root.render(
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 |
--------------------------------------------------------------------------------
/client/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Poppins:wght@100;300;400;500;600;700&display=swap");
2 | html {
3 | scroll-behavior: smooth;
4 | }
5 | body {
6 | margin: 0;
7 | padding: 0;
8 | font-family: "Poppins", sans-serif;
9 | -webkit-font-smoothing: antialiased;
10 | -moz-osx-font-smoothing: grayscale;
11 | }
12 |
13 | /* width */
14 | ::-webkit-scrollbar {
15 | width: 2px;
16 | }
17 | /* Track */
18 | ::-webkit-scrollbar-track {
19 | }
20 |
21 | /* Handle */
22 | ::-webkit-scrollbar-thumb {
23 | background: #888;
24 | border-radius: 6px;
25 | height: 50px;
26 | }
--------------------------------------------------------------------------------
/server/middleware/verifyToken.js:
--------------------------------------------------------------------------------
1 | import jwt from "jsonwebtoken";
2 | import { createError } from "../error.js";
3 |
4 | export const verifyToken = async (req, res, next) => {
5 | try {
6 | if (!req.headers.authorization) {
7 | return next(createError(401, "You are not authenticated!"));
8 | }
9 | const token = req.headers.authorization.split(" ")[1];
10 | if (!token) return next(createError(401, "You are not authenticated!"));
11 | const decode = jwt.verify(token, process.env.JWT);
12 | req.user = decode;
13 | return next();
14 | } catch (err) {
15 | next(err);
16 | }
17 | };
18 |
--------------------------------------------------------------------------------
/client/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/client/src/redux/reducers/snackbarSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | open: false,
5 | message: "",
6 | severity: "success",
7 | };
8 |
9 | export const snackbarSlice = createSlice({
10 | name: "snackbar",
11 | initialState,
12 | reducers: {
13 | openSnackbar: (state, action) => {
14 | state.open = true;
15 | state.message = action.payload.message;
16 | state.severity = action.payload.severity;
17 | },
18 | closeSnackbar: (state) => {
19 | state.open = false;
20 | },
21 | },
22 | });
23 |
24 | export const { openSnackbar, closeSnackbar } = snackbarSlice.actions;
25 | export default snackbarSlice.reducer;
26 |
--------------------------------------------------------------------------------
/client/src/utils/Themes.js:
--------------------------------------------------------------------------------
1 | export const lightTheme = {
2 | bg: "#FFFFFF",
3 | bgLight: "#FFFFFF",
4 | primary: "#131118",
5 | secondary: "#5B86E5",
6 |
7 | disabled: "#b1b2b3",
8 | menubar: "#191c29",
9 | navbar: "#242B3F",
10 | arrow: "#AFAFB5",
11 | menu_primary_text: "#F2F3F4",
12 | menu_secondary_text: "#b1b2b3",
13 | table_header: "#242445",
14 | text_primary: "#404040",
15 | text_secondary: "#4d4c4c",
16 | card: "#FFFFFF",
17 | black: "#000000",
18 | white: "#FFFFFF",
19 | shadow: "#00000020",
20 | green: "#00ff6a",
21 | yellow: "#e8ba00",
22 | red: "#ef5350",
23 | orange: "#F7AD63",
24 | popup: "#242B3F",
25 | popup_text_primary: "#F2F3F4",
26 | popup_text_secondary: "#b1b2b3",
27 | output_node: "#49516b",
28 | };
29 |
--------------------------------------------------------------------------------
/client/src/redux/reducers/userSlice.js:
--------------------------------------------------------------------------------
1 | import { createSlice } from "@reduxjs/toolkit";
2 |
3 | const initialState = {
4 | currentUser: null,
5 | };
6 |
7 | export const userSlice = createSlice({
8 | name: "user",
9 | initialState,
10 | reducers: {
11 | updateUser: (state, action) => {
12 | state.currentUser = action.payload.user;
13 | },
14 | loginSuccess: (state, action) => {
15 | state.currentUser = action.payload.user;
16 | localStorage.setItem("krist-app-token", action.payload.token);
17 | },
18 | logout: (state) => {
19 | state.currentUser = null;
20 | localStorage.removeItem("krist-app-token");
21 | },
22 | },
23 | });
24 |
25 | export const { updateUser, loginSuccess, logout } = userSlice.actions;
26 | export default userSlice.reducer;
27 |
--------------------------------------------------------------------------------
/client/src/components/ToastMessage.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import Alert from "@mui/material/Alert";
3 | import { useDispatch } from "react-redux";
4 | import Snackbar from "@mui/material/Snackbar";
5 | import { closeSnackbar } from "../redux/reducers/snackbarSlice";
6 |
7 | const ToastMessage = ({ message, severity, open }) => {
8 | const dispatch = useDispatch();
9 | return (
10 | dispatch(closeSnackbar())}
14 | >
15 | dispatch(closeSnackbar())}
17 | severity={severity}
18 | sx={{ width: "100%" }}
19 | >
20 | {message}
21 |
22 |
23 | );
24 | };
25 |
26 | export default ToastMessage;
27 |
28 |
29 | //You will get in drive file this code
--------------------------------------------------------------------------------
/server/models/Orders.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const OrdersSchema = new mongoose.Schema(
4 | {
5 | products: {
6 | type: [
7 | {
8 | product: { type: mongoose.Schema.Types.ObjectId, ref: "Products" },
9 | quantity: { type: Number, default: 1 },
10 | },
11 | ],
12 | required: true,
13 | },
14 | user: {
15 | type: mongoose.Schema.Types.ObjectId,
16 | ref: "User",
17 | required: true,
18 | },
19 | total_amount: {
20 | type: mongoose.Types.Decimal128,
21 | required: true,
22 | },
23 | address: {
24 | type: String,
25 | default: "",
26 | },
27 | status: {
28 | type: String,
29 | default: "Payment Done",
30 | },
31 | },
32 | { timestamps: true }
33 | );
34 |
35 | export default mongoose.model("Shopping-Orders", OrdersSchema);
36 |
--------------------------------------------------------------------------------
/server/models/Products.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const ProductsSchema = new mongoose.Schema(
4 | {
5 | title: {
6 | type: String,
7 | required: true,
8 | },
9 | name: {
10 | type: String,
11 | required: true,
12 | },
13 | desc: {
14 | type: String,
15 | required: true,
16 | },
17 | img: {
18 | type: String,
19 | required: true,
20 | },
21 | price: {
22 | type: {
23 | org: { type: Number, default: 0.0 },
24 | mrp: { type: Number, default: 0.0 },
25 | off: { type: Number, default: 0 },
26 | },
27 | default: { org: 0.0, mrp: 0.0, off: 0 },
28 | },
29 | sizes: {
30 | type: [String],
31 | default: [],
32 | },
33 | category: {
34 | type: [String],
35 | default: [],
36 | },
37 | },
38 | { timestamps: true }
39 | );
40 |
41 | export default mongoose.model("Products",ProductsSchema);
42 |
--------------------------------------------------------------------------------
/server/routes/User.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import {
3 | UserLogin,
4 | UserRegister,
5 | addToCart,
6 | addToFavorites,
7 | getAllCartItems,
8 | getAllOrders,
9 | getUserFavourites,
10 | placeOrder,
11 | removeFromCart,
12 | removeFromFavorites,
13 | } from "../controllers/User.js";
14 | import { verifyToken } from "../middleware/verifyToken.js";
15 |
16 | const router = express.Router();
17 |
18 | router.post("/signup", UserRegister);
19 | router.post("/signin", UserLogin);
20 |
21 | //cart
22 | router.get("/cart", verifyToken, getAllCartItems);
23 | router.post("/cart", verifyToken, addToCart);
24 | router.patch("/cart", verifyToken, removeFromCart);
25 |
26 | //order
27 | router.get("/order", verifyToken, getAllOrders);
28 | router.post("/order", verifyToken, placeOrder);
29 |
30 | //favourites
31 | router.get("/favorite", verifyToken, getUserFavourites);
32 | router.post("/favorite", verifyToken, addToFavorites);
33 | router.patch("/favorite", verifyToken, removeFromFavorites);
34 |
35 | export default router;
36 |
--------------------------------------------------------------------------------
/client/src/redux/store.js:
--------------------------------------------------------------------------------
1 | import { configureStore, combineReducers } from "@reduxjs/toolkit";
2 | import {
3 | persistStore,
4 | persistReducer,
5 | FLUSH,
6 | REHYDRATE,
7 | PAUSE,
8 | PERSIST,
9 | PURGE,
10 | REGISTER,
11 | } from "redux-persist";
12 | import storage from "redux-persist/lib/storage";
13 | import userReducer from "./reducers/userSlice";
14 | import snackbarReducer from "./reducers/snackbarSlice";
15 |
16 | const persistConfig = {
17 | key: "root",
18 | version: 1,
19 | storage,
20 | };
21 |
22 | const rootReducer = combineReducers({
23 | user: userReducer,
24 | snackbar: snackbarReducer,
25 | });
26 |
27 | const persistedReducer = persistReducer(persistConfig, rootReducer);
28 |
29 | export const store = configureStore({
30 | reducer: persistedReducer,
31 | middleware: (getDefaultMiddleware) =>
32 | getDefaultMiddleware({
33 | serializableCheck: {
34 | ignoreActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
35 | },
36 | }),
37 | });
38 |
39 | export const persistor = persistStore(store);
40 |
--------------------------------------------------------------------------------
/server/models/User.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 |
3 | const UserSchema = new mongoose.Schema(
4 | {
5 | name: {
6 | type: String,
7 | required: true,
8 | },
9 | email: {
10 | type: String,
11 | required: true,
12 | unique: true,
13 | },
14 | password: {
15 | type: String,
16 | required: true,
17 | },
18 | img: {
19 | type: String,
20 | default: null,
21 | },
22 | cart: {
23 | type: [
24 | {
25 | product: { type: mongoose.Schema.Types.ObjectId, ref: "Products" },
26 | quantity: { type: Number, default: 1 },
27 | },
28 | ],
29 | default: [],
30 | },
31 | favourites: {
32 | type: [mongoose.Schema.Types.ObjectId],
33 | ref: "Products",
34 | default: [],
35 | },
36 | orders: {
37 | type: [mongoose.Schema.Types.ObjectId],
38 | ref: "Shopping-Orders",
39 | default: [],
40 | },
41 | },
42 | { timestamps: true }
43 | );
44 |
45 | export default mongoose.model("User", UserSchema);
46 |
--------------------------------------------------------------------------------
/client/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "client",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@emotion/react": "^11.11.4",
7 | "@emotion/styled": "^11.11.0",
8 | "@mui/icons-material": "^5.15.13",
9 | "@mui/lab": "^5.0.0-alpha.168",
10 | "@mui/material": "^5.15.13",
11 | "@reduxjs/toolkit": "^2.2.1",
12 | "@testing-library/jest-dom": "^5.17.0",
13 | "@testing-library/react": "^13.4.0",
14 | "@testing-library/user-event": "^13.5.0",
15 | "axios": "^1.6.8",
16 | "react": "^18.2.0",
17 | "react-dom": "^18.2.0",
18 | "react-redux": "^9.1.0",
19 | "react-router-dom": "^6.22.3",
20 | "react-scripts": "5.0.1",
21 | "redux-persist": "^6.0.0",
22 | "styled-components": "^6.1.8",
23 | "web-vitals": "^2.1.4"
24 | },
25 | "scripts": {
26 | "start": "react-scripts start",
27 | "build": "react-scripts build",
28 | "test": "react-scripts test",
29 | "eject": "react-scripts eject"
30 | },
31 | "eslintConfig": {
32 | "extends": [
33 | "react-app",
34 | "react-app/jest"
35 | ]
36 | },
37 | "browserslist": {
38 | "production": [
39 | ">0.2%",
40 | "not dead",
41 | "not op_mini all"
42 | ],
43 | "development": [
44 | "last 1 chrome version",
45 | "last 1 firefox version",
46 | "last 1 safari version"
47 | ]
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/server/index.js:
--------------------------------------------------------------------------------
1 | import express from "express";
2 | import cors from "cors";
3 | import mongoose from "mongoose";
4 | import * as dotenv from "dotenv";
5 | import UserRouter from "./routes/User.js";
6 | import ProductRoutes from "./routes/Products.js";
7 | dotenv.config();
8 |
9 | const app = express();
10 | app.use(cors());
11 | app.use(express.json({ limit: "50mb" }));
12 | app.use(express.urlencoded({ extended: true }));
13 |
14 | //error handel
15 | app.use((err, req, res, next) => {
16 | const status = err.status || 500;
17 | const message = err.message || "Something went wrong";
18 | return res.status(status).json({
19 | success: false,
20 | status,
21 | message,
22 | });
23 | });
24 |
25 | app.get("/", async (req, res) => {
26 | res.status(200).json({
27 | message: "Hello GFG Developers",
28 | });
29 | });
30 |
31 | app.use("/api/user/", UserRouter);
32 | app.use("/api/products/", ProductRoutes);
33 |
34 | const connectDB = () => {
35 | mongoose.set("strictQuery", true);
36 | mongoose
37 | .connect(process.env.MODNO_DB)
38 | .then(() => console.log("Connected to MONGO DB"))
39 | .catch((err) => {
40 | console.error("failed to connect with mongo");
41 | console.error(err);
42 | });
43 | };
44 |
45 | const startServer = async () => {
46 | try {
47 | connectDB();
48 | app.listen(8080, () => console.log("Server started on port 8080"));
49 | } catch (error) {
50 | console.log(error);
51 | }
52 | };
53 |
54 | startServer();
55 |
--------------------------------------------------------------------------------
/client/src/utils/data.js:
--------------------------------------------------------------------------------
1 | export const category = [
2 | {
3 | img: "https://jaxsonmaximus.com/wp-content/uploads/2020/04/34394c211f01e58539f91e79e6ce1420.jpg",
4 | name: "Casual Wear",
5 | off: "20-40% OFF",
6 | },
7 | {
8 | img: "https://assets.myntassets.com/dpr_1.5,q_60,w_400,c_limit,fl_progressive/assets/images/24439364/2023/8/10/a157968c-bc17-4fa8-b805-31f930c47b381691681709790MenBlackSolidSlimFitFormalBlazer1.jpg",
9 | name: "Formal Wear",
10 | off: "10-20% OFF",
11 | },
12 | {
13 | img: "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQqR8nb8uO-HERwcu2eIWBFaSY7DC_KaX19hvBNApYgTgacS1Hhhx8WJOY_-jacJnwzXqY&usqp=CAU",
14 | name: "Winter Wear",
15 | off: "20-40% OFF",
16 | },
17 | {
18 | img: "https://images.bewakoof.com/utter/content/2484/content_western_dress_3.jpg",
19 | name: "Western Wear",
20 | off: "30-40% OFF",
21 | },
22 | {
23 | img: "https://assets0.mirraw.com/images/11782208/3283579_long_webp.webp?1696934926",
24 | name: "Ethnic Wear",
25 | off: "10-40% OFF",
26 | },
27 | ];
28 |
29 | export const filter = [
30 | {
31 | name: "Product Categories",
32 | value: "category",
33 | items: [
34 | "Men",
35 | "Women",
36 | "Kids",
37 | "Bags",
38 | "Accessories",
39 | "Casual Wear",
40 | "Formal Wear",
41 | "Winter Wear",
42 | "Ethnic Wear",
43 | ],
44 | },
45 | {
46 | name: "Filter by Price",
47 | value: "price",
48 | items: [],
49 | },
50 | {
51 | name: "Filter by Size",
52 | value: "size",
53 | items: ["S", "M", "L", "XL", "XXL"],
54 | },
55 | ];
56 |
--------------------------------------------------------------------------------
/client/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/client/src/api/index.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const API = axios.create({
4 | baseURL: "http://localhost:8080/api/",
5 | });
6 |
7 | export const UserSignUp = async (data) => await API.post("/user/signup", data);
8 | export const UserSignIn = async (data) => await API.post("/user/signin", data);
9 |
10 | //Products
11 | export const getAllProducts = async (filter) =>
12 | await API.get(`/products?${filter}`);
13 |
14 | export const getProductDetails = async (id) => await API.get(`/products/${id}`);
15 |
16 | //Cart
17 |
18 | export const getCart = async (token) =>
19 | await API.get("/user/cart", {
20 | headers: { Authorization: `Bearer ${token}` },
21 | });
22 |
23 | export const addToCart = async (token, data) =>
24 | await API.post(`/user/cart/`, data, {
25 | headers: { Authorization: `Bearer ${token}` },
26 | });
27 |
28 | export const deleteFromCart = async (token, data) =>
29 | await API.patch(`/user/cart/`, data, {
30 | headers: { Authorization: `Bearer ${token}` },
31 | });
32 |
33 | //Favourites
34 |
35 | export const getFavourite = async (token) =>
36 | await API.get(`/user/favorite`, {
37 | headers: { Authorization: `Bearer ${token}` },
38 | });
39 |
40 | export const addToFavourite = async (token, data) =>
41 | await API.post(`/user/favorite/`, data, {
42 | headers: { Authorization: `Bearer ${token}` },
43 | });
44 |
45 | export const deleteFromFavourite = async (token, data) =>
46 | await API.patch(`/user/favorite/`, data, {
47 | headers: { Authorization: `Bearer ${token}` },
48 | });
49 |
50 | //Orders
51 |
52 | export const placeOrder = async (token, data) =>
53 | await API.post(`/user/order/`, data, {
54 | headers: { Authorization: `Bearer ${token}` },
55 | });
56 |
57 | export const getOrders = async (token) =>
58 | await API.get(`/user/order/`, {
59 | headers: { Authorization: `Bearer ${token}` },
60 | });
61 |
--------------------------------------------------------------------------------
/client/src/App.js:
--------------------------------------------------------------------------------
1 | import styled, { ThemeProvider } from "styled-components";
2 | import { lightTheme } from "./utils/Themes";
3 | import { BrowserRouter, Route, Routes } from "react-router-dom";
4 | import Navbar from "./components/Navbar";
5 | import Home from "./pages/Home";
6 | import { useState } from "react";
7 | import Authentication from "./pages/Authentication";
8 | import ShopListing from "./pages/ShopListing";
9 | import Favourite from "./pages/Favourite";
10 | import Cart from "./pages/Cart";
11 | import ProductDetails from "./pages/ProductDetails";
12 | import { useDispatch, useSelector } from "react-redux";
13 | import ToastMessage from "./components/ToastMessage";
14 |
15 | const Container = styled.div`
16 | width: 100%;
17 | height: 100vh;
18 | display: flex;
19 | flex-direction: column;
20 | background: ${({ theme }) => theme.bg};
21 | color: ${({ theme }) => theme.text_primary};
22 | overflow-x: hidden;
23 | overflow-y: hidden;
24 | transition: all 0.2s ease;
25 | `;
26 |
27 | function App() {
28 | const { currentUser } = useSelector((state) => state.user);
29 | const { open, message, severity } = useSelector((state) => state.user);
30 | const [openAuth, setOpenAuth] = useState(false);
31 | return (
32 |
33 |
34 |
35 |
36 |
37 | } />
38 | } />
39 | } />
40 | } />
41 | } />
42 |
43 | {openAuth && (
44 |
45 | )}
46 | {open && (
47 |
48 | )}
49 |
50 |
51 |
52 | );
53 | }
54 |
55 | export default App;
56 |
--------------------------------------------------------------------------------
/client/src/components/cards/ProductCategoryCard.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import styled from "styled-components";
4 |
5 | const Card = styled.div`
6 | width: 250px;
7 | display: flex;
8 | flex-direction: column;
9 | gap: 16px;
10 | transition: all 0.3s ease-out;
11 | cursor: pointer;
12 | @media (max-width: 600px) {
13 | width: 170px;
14 | }
15 | `;
16 | const Image = styled.img`
17 | width: 100%;
18 | height: 320px;
19 | border-radius: 6px;
20 | object-fit: cover;
21 | transition: all 0.3s ease-out;
22 | @media (max-width: 600px) {
23 | height: 240px;
24 | }
25 | `;
26 | const Top = styled.div`
27 | display: flex;
28 | align-items: center;
29 | justify-content: center;
30 | position: relative;
31 | border-radius: 6px;
32 | transition: all 0.3s ease-out;
33 | &:hover {
34 | background-color: ${({ theme }) => theme.primary};
35 | }
36 | &:hover ${Image} {
37 | opacity: 0.8;
38 | }
39 | `;
40 | const Menu = styled.div`
41 | width: 90%;
42 | position: absolute;
43 | z-index: 10;
44 | color: ${({ theme }) => theme.text_primary};
45 | bottom: 20px;
46 | left: 50;
47 | right: 50;
48 | display: flex;
49 | gap: 12px;
50 | `;
51 | const Button = styled.div`
52 | width: 100%;
53 | color: ${({ theme }) => theme.primary};
54 | padding: 12px 20px;
55 | background: white;
56 | border-radius: 12px;
57 | text-align: center;
58 | font-weight: 500;
59 | @media (max-width: 600px) {
60 | padding: 6px 14px;
61 | }
62 | `;
63 | const Sale = styled.div`
64 | position: absolute;
65 | z-index: 10;
66 | color: ${({ theme }) => theme.text_primary};
67 | top: 10px;
68 | right: 10px;
69 | font-size: 12px;
70 | font-weight: 600;
71 | color: white;
72 | background: green;
73 | padding: 3px 6px;
74 | border-radius: 4px;
75 | @media (max-width: 600px) {
76 | font-size: 10px;
77 | }
78 | `;
79 |
80 | const ProductCategoryCard = ({ category }) => {
81 | const navigate = useNavigate();
82 | return (
83 | navigate(`/shop?category=${category.name}`)}>
84 |
85 |
86 |
89 | {category.off}
90 |
91 |
92 | );
93 | };
94 |
95 | export default ProductCategoryCard;
96 |
--------------------------------------------------------------------------------
/client/src/components/Button.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress } from "@mui/material";
2 | import React from "react";
3 | import styled from "styled-components";
4 |
5 | const Button = styled.div`
6 | border-radius: 10px;
7 | color: white;
8 | font-size: 14px;
9 | cursor: pointer;
10 | transition: all 0.3s ease;
11 | display: flex;
12 | align-items: center;
13 | justify-content: center;
14 | gap: 6px;
15 | height: min-content;
16 | padding: 16px 26px;
17 | box-shadow: 1px 20px 35px 0px ${({ theme }) => theme.primary + 40};
18 | border: 1px solid ${({ theme }) => theme.primary};
19 | @media (max-width: 600px) {
20 | padding: 8px 12px;
21 | }
22 |
23 | ${({ type, theme }) =>
24 | type === "secondary"
25 | ? `
26 | background: ${theme.secondary};
27 | border: 1px solid ${({ theme }) => theme.secondary};
28 | `
29 | : `
30 | background: ${theme.primary};
31 | `}
32 |
33 | ${({ isDisabled }) =>
34 | isDisabled &&
35 | `
36 | opacity: 0.8;
37 | cursor: not-allowed;
38 |
39 | `}
40 | ${({ isLoading }) =>
41 | isLoading &&
42 | `
43 | opacity: 0.8;
44 | cursor: not-allowed;
45 | `}
46 | ${({ flex }) =>
47 | flex &&
48 | `
49 | flex: 1;
50 | `}
51 |
52 | ${({ small }) =>
53 | small &&
54 | `
55 | padding: 10px 28px;
56 | `}
57 | ${({ outlined, theme }) =>
58 | outlined &&
59 | `
60 | background: transparent;
61 | color: ${theme.primary};
62 | box-shadow: none;
63 | `}
64 | ${({ full }) =>
65 | full &&
66 | `
67 | width: 100%;`}
68 | `;
69 |
70 | const button = ({
71 | text,
72 | isLoading,
73 | isDisabled,
74 | rightIcon,
75 | leftIcon,
76 | type,
77 | onClick,
78 | flex,
79 | small,
80 | outlined,
81 | full,
82 | }) => {
83 | return (
84 |
104 | );
105 | };
106 |
107 | export default button;
108 |
--------------------------------------------------------------------------------
/client/src/pages/Favourite.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import ProductCard from "../components/cards/ProductCard";
4 | import { getFavourite } from "../api";
5 | import { CircularProgress } from "@mui/material";
6 |
7 | const Container = styled.div`
8 | padding: 20px 30px;
9 | padding-bottom: 200px;
10 | height: 100%;
11 | overflow-y: scroll;
12 | display: flex;
13 | align-items: center;
14 | flex-direction: column;
15 | gap: 30px;
16 | @media (max-width: 768px) {
17 | padding: 20px 12px;
18 | }
19 | background: ${({ theme }) => theme.bg};
20 | `;
21 | const Section = styled.div`
22 | max-width: 1400px;
23 | padding: 32px 16px;
24 | display: flex;
25 | flex-direction: column;
26 | gap: 28px;
27 | `;
28 |
29 | const Title = styled.div`
30 | font-size: 28px;
31 | font-weight: 500;
32 | display: flex;
33 | justify-content: ${({ center }) => (center ? "center" : "space-between")};
34 | align-items: center;
35 | `;
36 |
37 | const CardWrapper = styled.div`
38 | display: flex;
39 | flex-wrap: wrap;
40 | gap: 24px;
41 | justify-content: center;
42 | @media (max-width: 750px) {
43 | gap: 14px;
44 | }
45 | `;
46 |
47 | const Favourite = () => {
48 | const [loading, setLoading] = useState(false);
49 | const [products, setProducts] = useState([]);
50 | const [reload, setReload] = useState(false);
51 |
52 | const getProducts = async () => {
53 | setLoading(true);
54 | const token = localStorage.getItem("krist-app-token");
55 | await getFavourite(token).then((res) => {
56 | setProducts(res.data);
57 | setLoading(false);
58 | setReload(!reload);
59 | });
60 | };
61 |
62 | useEffect(() => {
63 | getProducts();
64 | }, []);
65 | return (
66 |
67 |
68 | Your favourites
69 |
70 | {loading ? (
71 |
72 | ) : (
73 | <>
74 | {products.length === 0 ? (
75 | <>No Products>
76 | ) : (
77 |
78 | {products.map((product) => (
79 |
80 | ))}
81 |
82 | )}
83 | >
84 | )}
85 |
86 |
87 |
88 | );
89 | };
90 |
91 | export default Favourite;
92 |
--------------------------------------------------------------------------------
/client/src/pages/Home.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import HeaderImage from "../utils/Images/Header.png";
4 | import { category } from "../utils/data";
5 | import ProductCategoryCard from "../components/cards/ProductCategoryCard";
6 | import ProductCard from "../components/cards/ProductCard";
7 | import { getAllProducts } from "../api";
8 |
9 | const Container = styled.div`
10 | padding: 20px 30px;
11 | padding-bottom: 200px;
12 | height: 100%;
13 | overflow-y: scroll;
14 | display: flex;
15 | align-items: center;
16 | flex-direction: column;
17 | gap: 30px;
18 | @media (max-width: 768px) {
19 | padding: 20px 12px;
20 | }
21 | background: ${({ theme }) => theme.bg};
22 | `;
23 | const Section = styled.div`
24 | max-width: 1400px;
25 | padding: 32px 16px;
26 | display: flex;
27 | flex-direction: column;
28 | gap: 28px;
29 | `;
30 | const Img = styled.img`
31 | width: 90%;
32 | height: 700px;
33 | object-fit: cover;
34 | max-width: 1200px;
35 | `;
36 |
37 | const Title = styled.div`
38 | font-size: 28px;
39 | font-weight: 500;
40 | display: flex;
41 | justify-content: ${({ center }) => (center ? "center" : "space-between")};
42 | align-items: center;
43 | `;
44 |
45 | const CardWrapper = styled.div`
46 | display: flex;
47 | flex-wrap: wrap;
48 | gap: 24px;
49 | justify-content: center;
50 | @media (max-width: 750px) {
51 | gap: 14px;
52 | }
53 | `;
54 |
55 | const Home = () => {
56 | const [loading, setLoading] = useState(false);
57 | const [products, setProducts] = useState([]);
58 |
59 | const getProducts = async () => {
60 | setLoading(true);
61 | await getAllProducts().then((res) => {
62 | setProducts(res.data);
63 | setLoading(false);
64 | });
65 | };
66 |
67 | useEffect(() => {
68 | getProducts();
69 | }, []);
70 | return (
71 |
72 |
77 |
78 |
79 |
80 | Shop by Categories
81 |
82 | {category.map((category) => (
83 |
84 | ))}
85 |
86 |
87 |
88 | Our Bestseller
89 |
90 | {products.map((product) => (
91 |
92 | ))}
93 |
94 |
95 |
96 | );
97 | };
98 |
99 | export default Home;
100 |
--------------------------------------------------------------------------------
/server/controllers/Products.js:
--------------------------------------------------------------------------------
1 | import mongoose from "mongoose";
2 | import Products from "../models/Products.js";
3 | import { createError } from "../error.js";
4 |
5 | export const addProducts = async (req, res, next) => {
6 | try {
7 | const productsData = req.body;
8 |
9 | if (!Array.isArray(productsData)) {
10 | return next(
11 | createError(400, "Invalid request. Expected an array of products")
12 | );
13 | }
14 |
15 | const createdproducts = [];
16 |
17 | for (const productInfo of productsData) {
18 | const { title, name, desc, img, price, sizes, category } = productInfo;
19 |
20 | const product = new Products({
21 | title,
22 | name,
23 | desc,
24 | img,
25 | price,
26 | sizes,
27 | category,
28 | });
29 | const createdproduct = await product.save();
30 |
31 | createdproducts.push(createdproduct);
32 | }
33 |
34 | return res
35 | .status(201)
36 | .json({ message: "Products added successfully", createdproducts });
37 | } catch (err) {
38 | next(err);
39 | }
40 | };
41 |
42 | export const getproducts = async (req, res, next) => {
43 | try {
44 | let { categories, minPrice, maxPrice, sizes, search } = req.query;
45 | sizes = sizes?.split(",");
46 | categories = categories?.split(",");
47 |
48 | const filter = {};
49 |
50 | if (categories && Array.isArray(categories)) {
51 | filter.category = { $in: categories }; // Match products in any of the specified categories
52 | }
53 |
54 | if (minPrice || maxPrice) {
55 | filter["price.org"] = {};
56 | if (minPrice) {
57 | filter["price.org"]["$gte"] = parseFloat(minPrice);
58 | }
59 | if (maxPrice) {
60 | filter["price.org"]["$lte"] = parseFloat(maxPrice);
61 | }
62 | }
63 |
64 | if (sizes && Array.isArray(sizes)) {
65 | filter.sizes = { $in: sizes }; // Match products in any of the specified sizes
66 | }
67 |
68 | if (search) {
69 | filter.$or = [
70 | { title: { $regex: new RegExp(search, "i") } }, // Case-insensitive title search
71 | { desc: { $regex: new RegExp(search, "i") } }, // Case-insensitive description search
72 | ];
73 | }
74 |
75 | const products = await Products.find(filter);
76 | return res.status(200).json(products);
77 | } catch (err) {
78 | next(err);
79 | }
80 | };
81 |
82 | export const getProductById = async (req, res, next) => {
83 | try {
84 | const { id } = req.params;
85 | if (!mongoose.isValidObjectId(id)) {
86 | return next(createError(400, "Invalid product ID"));
87 | }
88 | const product = await Products.findById(id);
89 | if (!product) {
90 | return next(createError(404, "Product not found"));
91 | }
92 | return res.status(200).json(product);
93 | } catch (err) {
94 | return next(error);
95 | }
96 | };
97 |
--------------------------------------------------------------------------------
/client/src/pages/Authentication.jsx:
--------------------------------------------------------------------------------
1 | import { Modal } from "@mui/material";
2 | import React, { useState } from "react";
3 | import styled from "styled-components";
4 | import LogoImage from "../utils/Images/Logo.png";
5 | import AuthImage from "../utils/Images/AuthImage.png";
6 | import { Close } from "@mui/icons-material";
7 | import SignIn from "../components/SignIn";
8 | import SignUp from "../components/SignUp";
9 |
10 | const Container = styled.div`
11 | flex: 1;
12 | height: 100%;
13 | display: flex;
14 | background: ${({ theme }) => theme.bg};
15 | `;
16 | const Left = styled.div`
17 | flex: 1;
18 | position: relative;
19 | @media screen and (max-width: 768px) {
20 | display: none;
21 | }
22 | `;
23 |
24 | const Logo = styled.img`
25 | position: absolute;
26 | top: 40px;
27 | left: 60px;
28 | z-index: 10;
29 | `;
30 | const Image = styled.img`
31 | position: relative;
32 | height: 100%;
33 | width: 100%;
34 | object-fit: cover;
35 | `;
36 |
37 | const Right = styled.div`
38 | position: relative;
39 | flex: 0.9;
40 | display: flex;
41 | flex-direction: column;
42 | padding: 40px;
43 | gap: 16px;
44 | align-items: center;
45 | justify-content: center;
46 | @media screen and (max-width: 768px) {
47 | flex: 1;
48 | }
49 | `;
50 |
51 | const CloseButton = styled.div`
52 | position: absolute;
53 | top: 20px;
54 | right: 20px;
55 | border-radius: 50%;
56 | padding: 2px;
57 | width: 32px;
58 | height: 32px;
59 | border: 1px solid ${({ theme }) => theme.primary};
60 | display: flex;
61 | justify-content: center;
62 | align-items: center;
63 | &:hover {
64 | background: ${({ theme }) => theme.primary + 20};
65 | }
66 | `;
67 |
68 | const Text = styled.p`
69 | display: flex;
70 | gap: 12px;
71 | font-size: 16px;
72 | text-align: center;
73 | color: ${({ theme }) => theme.text_secondary};
74 | margin-top: 16px;
75 | @media (max-width: 400px) {
76 | font-size: 14px;
77 | }
78 | `;
79 | const TextButton = styled.div`
80 | color: ${({ theme }) => theme.primary};
81 | cursor: pointer;
82 | transition: all 0.3s ease;
83 | font-weight: 600;
84 | `;
85 |
86 | const Authentication = ({ openAuth, setOpenAuth }) => {
87 | const [login, setLogin] = useState(true);
88 | return (
89 | setOpenAuth(false)}>
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 | setOpenAuth(false)} />
98 |
99 | {login ? (
100 | <>
101 |
102 |
103 | {" "}
104 | Don't have an account ?{" "}
105 | setLogin(false)}>Sign Up
106 |
107 | >
108 | ) : (
109 | <>
110 |
111 |
112 | Already have an account ?{" "}
113 | setLogin(true)}>Sign In
114 |
115 | >
116 | )}
117 |
118 |
119 |
120 | );
121 | };
122 |
123 | export default Authentication;
124 |
--------------------------------------------------------------------------------
/client/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `npm start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
13 |
14 | The page will reload when you make changes.\
15 | You may also see any lint errors in the console.
16 |
17 | ### `npm test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `npm run build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `npm run eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!**
35 |
36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
39 |
40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
48 | ### Code Splitting
49 |
50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
51 |
52 | ### Analyzing the Bundle Size
53 |
54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
55 |
56 | ### Making a Progressive Web App
57 |
58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
59 |
60 | ### Advanced Configuration
61 |
62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
63 |
64 | ### Deployment
65 |
66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
67 |
68 | ### `npm run build` fails to minify
69 |
70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
71 |
--------------------------------------------------------------------------------
/client/src/components/SignIn.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import styled from "styled-components";
3 | import TextInput from "./TextInput";
4 | import Button from "./Button";
5 | import { UserSignIn } from "../api";
6 | import { useDispatch } from "react-redux";
7 | import { loginSuccess } from "../redux/reducers/userSlice";
8 | import { openSnackbar } from "../redux/reducers/snackbarSlice";
9 |
10 | const Container = styled.div`
11 | width: 100%;
12 | max-width: 500px;
13 | display: flex;
14 | flex-direction: column;
15 | gap: 36px;
16 | `;
17 | const Title = styled.div`
18 | font-size: 30px;
19 | font-weight: 800;
20 | color: ${({ theme }) => theme.primary};
21 | `;
22 | const Span = styled.div`
23 | font-size: 16px;
24 | font-weight: 400;
25 | color: ${({ theme }) => theme.text_secondary + 90};
26 | `;
27 | const TextButton = styled.div`
28 | width: 100%;
29 | text-align: end;
30 | color: ${({ theme }) => theme.text_primary};
31 | cursor: pointer;
32 | font-size: 14px;
33 | transition: all 0.3s ease;
34 | font-weight: 500;
35 | &:hover {
36 | color: ${({ theme }) => theme.primary};
37 | }
38 | `;
39 |
40 | const SignIn = () => {
41 | const dispatch = useDispatch();
42 | const [buttonLoading, setButtonLoading] = useState(false);
43 | const [buttonDisabled, setButtonDisabled] = useState(false);
44 | const [email, setEmail] = useState("");
45 | const [password, setPassword] = useState("");
46 |
47 | const validateInputs = () => {
48 | if (!email || !password) {
49 | alert("Please fill in all fields");
50 | return false;
51 | }
52 | return true;
53 | };
54 |
55 | const handelSignIn = async () => {
56 | setButtonLoading(true);
57 | setButtonDisabled(true);
58 | if (validateInputs()) {
59 | await UserSignIn({ email, password })
60 | .then((res) => {
61 | dispatch(loginSuccess(res.data));
62 | dispatch(
63 | openSnackbar({
64 | message: "Login Successful",
65 | severity: "success",
66 | })
67 | );
68 | })
69 | .catch((err) => {
70 | if (err.response) {
71 | setButtonLoading(false);
72 | setButtonDisabled(false);
73 | alert(err.response.data.message);
74 | dispatch(
75 | openSnackbar({
76 | message: err.response.data.message,
77 | severity: "error",
78 | })
79 | );
80 | } else {
81 | setButtonLoading(false);
82 | setButtonDisabled(false);
83 | dispatch(
84 | openSnackbar({
85 | message: err.message,
86 | severity: "error",
87 | })
88 | );
89 | }
90 | });
91 | }
92 | setButtonDisabled(false);
93 | setButtonLoading(false);
94 | };
95 |
96 | return (
97 |
98 |
99 |
Welcome to Krist 👋
100 | Please login with your details here
101 |
102 |
103 | setEmail(e.target.value)}
108 | />
109 | setPassword(e.target.value)}
115 | />
116 |
117 | Forgot Password?
118 |
124 |
125 |
126 | );
127 | };
128 |
129 | export default SignIn;
130 |
--------------------------------------------------------------------------------
/client/src/components/SignUp.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import styled from "styled-components";
3 | import TextInput from "./TextInput";
4 | import Button from "./Button";
5 | import { UserSignUp } from "../api";
6 | import { useDispatch } from "react-redux";
7 | import { loginSuccess } from "../redux/reducers/userSlice";
8 | import { openSnackbar } from "../redux/reducers/snackbarSlice";
9 |
10 | const Container = styled.div`
11 | width: 100%;
12 | max-width: 500px;
13 | display: flex;
14 | flex-direction: column;
15 | gap: 36px;
16 | `;
17 | const Title = styled.div`
18 | font-size: 30px;
19 | font-weight: 800;
20 | color: ${({ theme }) => theme.primary};
21 | `;
22 | const Span = styled.div`
23 | font-size: 16px;
24 | font-weight: 400;
25 | color: ${({ theme }) => theme.text_secondary + 90};
26 | `;
27 |
28 | const SignUp = ({ setOpenAuth }) => {
29 | const dispatch = useDispatch();
30 | const [loading, setLoading] = useState(false);
31 | const [buttonDisabled, setButtonDisabled] = useState(false);
32 | const [name, setName] = useState("");
33 | const [email, setEmail] = useState("");
34 | const [password, setPassword] = useState("");
35 |
36 | const validateInputs = () => {
37 | if (!name || !email || !password) {
38 | alert("Please fill in all fields");
39 | return false;
40 | }
41 | return true;
42 | };
43 |
44 | const handleSignUp = async () => {
45 | setLoading(true);
46 | setButtonDisabled(true);
47 | if (validateInputs()) {
48 | await UserSignUp({ name, email, password })
49 | .then((res) => {
50 | dispatch(loginSuccess(res.data));
51 | dispatch(
52 | openSnackbar({
53 | message: "Sign Up Successful",
54 | severity: "success",
55 | })
56 | );
57 | setLoading(false);
58 | setButtonDisabled(false);
59 | setOpenAuth(false);
60 | })
61 | .catch((err) => {
62 | setButtonDisabled(false);
63 | if (err.response) {
64 | setLoading(false);
65 | setButtonDisabled(false);
66 | alert(err.response.data.message);
67 | dispatch(
68 | openSnackbar({
69 | message: err.response.data.message,
70 | severity: "error",
71 | })
72 | );
73 | } else {
74 | setLoading(false);
75 | setButtonDisabled(false);
76 | dispatch(
77 | openSnackbar({
78 | message: err.message,
79 | severity: "error",
80 | })
81 | );
82 | }
83 | });
84 | }
85 |
86 | setButtonDisabled(false);
87 | setLoading(false);
88 | };
89 |
90 | return (
91 |
92 |
93 |
Create New Account 👋
94 | Please enter details to create a new account
95 |
96 |
97 | setName(e.target.value)}
102 | />
103 | setEmail(e.target.value)}
108 | />
109 | setPassword(e.target.value)}
115 | />
116 |
122 |
123 |
124 | );
125 | };
126 |
127 | export default SignUp;
128 |
--------------------------------------------------------------------------------
/client/src/components/TextInput.jsx:
--------------------------------------------------------------------------------
1 | import { CloseRounded, Visibility, VisibilityOff } from "@mui/icons-material";
2 | import React, { useState } from "react";
3 | import styled from "styled-components";
4 |
5 | const Container = styled.div`
6 | flex: 1;
7 | display: flex;
8 | flex-direction: column;
9 | gap: 6px;
10 | `;
11 |
12 | const Label = styled.label`
13 | font-size: 12px;
14 | color: ${({ theme }) => theme.primary};
15 | padding: 0px 4px;
16 | ${({ error, theme }) =>
17 | error &&
18 | `
19 | color: ${theme.red};
20 | `}
21 | ${({ small }) =>
22 | small &&
23 | `
24 | font-size: 8px;
25 | `}
26 | ${({ popup, theme }) =>
27 | popup &&
28 | `
29 | color: ${theme.popup_text_secondary};
30 | `}
31 | `;
32 |
33 | const OutlinedInput = styled.div`
34 | border-radius: 8px;
35 | border: 0.5px solid ${({ theme }) => theme.text_secondary};
36 | background-color: transparent;
37 | color: ${({ theme }) => theme.text_primary};
38 | outline: none;
39 | padding: 16px;
40 | display: flex;
41 | align-items: center;
42 | gap: 12px;
43 | &:focus-within {
44 | border-color: ${({ theme }) => theme.secondary};
45 | }
46 | ${({ error, theme }) =>
47 | error &&
48 | `
49 | border-color: ${theme.red};
50 | `}
51 |
52 | ${({ chipableInput, height, theme }) =>
53 | chipableInput &&
54 | `
55 | background: ${theme.card};
56 | flex-direction: column;
57 | align-items: flex-start;
58 | gap: 8px;
59 | min-height: ${height}
60 | `}
61 |
62 | ${({ small }) =>
63 | small &&
64 | `
65 | border-radius: 6px;
66 | padding: 8px 10px;
67 | `}
68 |
69 | ${({ popup, theme }) =>
70 | popup &&
71 | `
72 | color: ${theme.popup_text_secondary};
73 | border: 0.5px solid ${theme.popup_text_secondary + 60};
74 | `}
75 | `;
76 |
77 | const Input = styled.input`
78 | width: 100%;
79 | font-size: 14px;
80 | outline: none;
81 | border: none;
82 | background-color: transparent;
83 | color: ${({ theme }) => theme.primary};
84 | &:focus {
85 | outline: none;
86 | }
87 | ${({ small }) =>
88 | small &&
89 | `
90 | font-size: 12px;
91 | `}
92 |
93 | ${({ popup, theme }) =>
94 | popup &&
95 | `
96 | color: ${theme.popup_text_secondary};
97 | `} ${({ theme }) => theme.popup_text_secondary};
98 | `;
99 |
100 | const Error = styled.p`
101 | font-size: 12px;
102 | margin: 0px 4px;
103 | color: ${({ theme }) => theme.red};
104 | ${({ small }) =>
105 | small &&
106 | `
107 | font-size: 8px;
108 | `}
109 | `;
110 |
111 | const ChipWrapper = styled.div`
112 | display: flex;
113 | flex-wrap: wrap;
114 | gap: 6px;
115 | `;
116 |
117 | const Chip = styled.div`
118 | padding: 5px 10px;
119 | border-radius: 8px;
120 | background: ${({ theme }) => theme.primary + 10};
121 | color: ${({ theme }) => theme.primary};
122 | font-size: 12px;
123 | display: flex;
124 | align-items: center;
125 | gap: 4px;
126 | cursor: pointer;
127 | transition: all 0.3s ease;
128 | `;
129 |
130 | const TextInput = ({
131 | label,
132 | placeholder,
133 | name,
134 | value,
135 | error,
136 | handelChange,
137 | textArea,
138 | rows,
139 | columns,
140 | chipableInput,
141 | chipableArray,
142 | removeChip,
143 | height,
144 | small,
145 | popup,
146 | password,
147 | }) => {
148 | const [showPassword, setShowPassword] = useState(false);
149 | return (
150 |
151 |
154 |
161 | {chipableInput ? (
162 |
163 | {chipableArray.map((chip, index) => (
164 |
165 | {chip}
166 | removeChip(name, index)}
169 | />
170 |
171 | ))}
172 | handelChange(e)}
177 | />
178 |
179 | ) : (
180 | <>
181 | handelChange(e)}
191 | type={password && !showPassword ? "password" : "text"}
192 | />
193 | {password && (
194 | <>
195 | {showPassword ? (
196 | <>
197 | setShowPassword(false)} />
198 | >
199 | ) : (
200 | <>
201 | setShowPassword(true)} />
202 | >
203 | )}
204 | >
205 | )}
206 | >
207 | )}
208 |
209 | {error && (
210 |
211 | {error}
212 |
213 | )}
214 |
215 | );
216 | };
217 |
218 | export default TextInput;
219 |
--------------------------------------------------------------------------------
/server/controllers/User.js:
--------------------------------------------------------------------------------
1 | import bcrypt from "bcrypt";
2 | import jwt from "jsonwebtoken";
3 | import dotenv from "dotenv";
4 | import { createError } from "../error.js";
5 | import User from "../models/User.js";
6 | import Orders from "../models/Orders.js";
7 |
8 | dotenv.config();
9 |
10 | //user register controller
11 | export const UserRegister = async (req, res, next) => {
12 | try {
13 | const { email, password, name, img } = req.body;
14 | const existingUser = await User.findOne({ email });
15 | if (existingUser) {
16 | return next(createError(409, "Email is already in use"));
17 | }
18 | const salt = bcrypt.genSaltSync(10);
19 | const hashedpassword = bcrypt.hashSync(password, salt);
20 |
21 | const user = new User({
22 | name,
23 | email,
24 | password: hashedpassword,
25 | img,
26 | });
27 | const createduser = user.save();
28 | const token = jwt.sign({ id: createduser._id }, process.env.JWT, {
29 | expiresIn: "9999 years",
30 | });
31 | return res.status(200).json({ token, user });
32 | } catch (error) {
33 | return next(error);
34 | }
35 | };
36 |
37 | //user login controller
38 | export const UserLogin = async (req, res, next) => {
39 | try {
40 | const { email, password } = req.body;
41 | console.log(email);
42 | const existingUser = await User.findOne({ email });
43 | if (!existingUser) {
44 | return next(createError(404, "user not found"));
45 | }
46 |
47 | const isPasswordCorrect = await bcrypt.compareSync(
48 | password,
49 | existingUser.password
50 | );
51 | if (!isPasswordCorrect) {
52 | return next(createError(403, "Incorrect password"));
53 | }
54 | const token = jwt.sign({ id: existingUser._id }, process.env.JWT, {
55 | expiresIn: "9999 years",
56 | });
57 | return res.status(200).json({ token, user: existingUser });
58 | } catch (error) {
59 | return next(error);
60 | }
61 | };
62 |
63 | // Cart
64 | export const addToCart = async (req, res, next) => {
65 | try {
66 | const { productId, quantity } = req.body;
67 | const userJWT = req.user;
68 | const user = await User.findById(userJWT.id);
69 | const existingCartItemIndex = user.cart.findIndex((item) =>
70 | item?.product?.equals(productId)
71 | );
72 | if (existingCartItemIndex !== -1) {
73 | // Product is already in the cart, update the quantity
74 | user.cart[existingCartItemIndex].quantity += quantity;
75 | } else {
76 | // Product is not in the cart, add it
77 | user.cart.push({ product: productId, quantity });
78 | }
79 | await user.save();
80 |
81 | return res
82 | .status(200)
83 | .json({ message: "Product added to cart successfully", user });
84 | } catch (err) {
85 | next(err);
86 | }
87 | };
88 |
89 | export const removeFromCart = async (req, res, next) => {
90 | try {
91 | const { productId, quantity } = req.body;
92 | const userJWT = req.user;
93 | const user = await User.findById(userJWT.id);
94 | if (!user) {
95 | return next(createError(404, "User not found"));
96 | }
97 | const productIndex = user.cart.findIndex((item) =>
98 | item.product.equals(productId)
99 | );
100 | if (productIndex !== -1) {
101 | if (quantity && quantity > 0) {
102 | user.cart[productIndex].quantity -= quantity;
103 | if (user.cart[productIndex].quantity <= 0) {
104 | user.cart.splice(productIndex, 1);
105 | }
106 | } else {
107 | user.cart.splice(productIndex, 1);
108 | }
109 |
110 | await user.save();
111 | return res
112 | .status(200)
113 | .json({ message: "Product quantity updated in cart", user });
114 | } else {
115 | return next(createError(404, "Product not found in the user's cart"));
116 | }
117 | } catch (err) {
118 | next(err);
119 | }
120 | };
121 |
122 | export const getAllCartItems = async (req, res, next) => {
123 | try {
124 | const userJWT = req.user;
125 | const user = await User.findById(userJWT.id).populate({
126 | path: "cart.product",
127 | model: "Products",
128 | });
129 | const cartItems = user.cart;
130 | return res.status(200).json(cartItems);
131 | } catch (err) {
132 | next(err);
133 | }
134 | };
135 |
136 | // Order
137 |
138 | export const placeOrder = async (req, res, next) => {
139 | try {
140 | const { products, address, totalAmount } = req.body;
141 | const userJWT = req.user;
142 | const user = await User.findById(userJWT.id);
143 | const order = new Orders({
144 | products,
145 | user: user._id,
146 | total_amount: totalAmount,
147 | address,
148 | });
149 | await order.save();
150 |
151 | user.cart.save();
152 |
153 | user.cart = [];
154 | await user.save();
155 |
156 | return res
157 | .status(200)
158 | .json({ message: "Order placed successfully", order });
159 | } catch (err) {
160 | next(err);
161 | }
162 | };
163 |
164 | export const getAllOrders = async (req, res, next) => {
165 | try {
166 | const user = req.user;
167 | const orders = await Orders.find({ user: user.id });
168 | return res.status(200).json(orders);
169 | } catch (err) {
170 | next(err);
171 | }
172 | };
173 |
174 | //Favourite
175 |
176 | export const addToFavorites = async (req, res, next) => {
177 | try {
178 | const { productId } = req.body;
179 | const userJWT = req.user;
180 | const user = await User.findById(userJWT.id);
181 |
182 | if (!user.favourites.includes(productId)) {
183 | user.favourites.push(productId);
184 | await user.save();
185 | }
186 |
187 | return res
188 | .status(200)
189 | .json({ message: "Product added to favorites successfully", user });
190 | } catch (err) {
191 | next(err);
192 | }
193 | };
194 |
195 | export const removeFromFavorites = async (req, res, next) => {
196 | try {
197 | const { productId } = req.body;
198 | const userJWT = req.user;
199 | const user = await User.findById(userJWT.id);
200 |
201 | user.favourites = user.favourites.filter((fav) => !fav.equals(productId));
202 | await user.save();
203 | return res
204 | .status(200)
205 | .json({ message: "Product removed from favorites successfully", user });
206 | } catch (err) {
207 | next(err);
208 | }
209 | };
210 |
211 | export const getUserFavourites = async (req, res, next) => {
212 | try {
213 | const userId = req.user.id;
214 | const user = await User.findById(userId).populate("favourites").exec();
215 |
216 | if (!user) {
217 | return next(createError(404, "User not found"));
218 | }
219 |
220 | return res.status(200).json(user.favourites);
221 | } catch (err) {
222 | next(err);
223 | }
224 | };
225 |
--------------------------------------------------------------------------------
/client/src/pages/ShopListing.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import ProductCard from "../components/cards/ProductCard";
3 | import styled from "styled-components";
4 | import { category, filter } from "../utils/data";
5 | import { CircularProgress, Slider } from "@mui/material";
6 | import { getAllProducts } from "../api";
7 |
8 | const Container = styled.div`
9 | padding: 20px 30px;
10 | height: 100vh;
11 | overflow-y: hidden;
12 | display: flex;
13 | align-items: center;
14 | gap: 30px;
15 | @media (max-width: 768px) {
16 | padding: 20px 12px;
17 | flex-direction: column;
18 | overflow-y: scroll;
19 | }
20 | background: ${({ theme }) => theme.bg};
21 | `;
22 | const Filters = styled.div`
23 | width: 100%;
24 | height: fit-content;
25 | overflow-y: hidden;
26 | padding: 20px 16px;
27 | @media (min-width: 768px) {
28 | height: 100%;
29 | width: 230px;
30 | overflow-y: scroll;
31 | }
32 | `;
33 | const FilterSection = styled.div`
34 | display: flex;
35 | flex-direction: column;
36 | gap: 16px;
37 | padding: 12px;
38 | `;
39 | const Title = styled.div`
40 | font-size: 20px;
41 | font-weight: 500;
42 | `;
43 | const Menu = styled.div`
44 | display: flex;
45 | flex-direction: column;
46 | gap: 4px;
47 | `;
48 | const Products = styled.div`
49 | padding: 12px;
50 | overflow: hidden;
51 | height: fit-content;
52 | @media (min-width: 768px) {
53 | width: 100%;
54 | overflow-y: scroll;
55 | height: 100%;
56 | }
57 | `;
58 | const CardWrapper = styled.div`
59 | display: flex;
60 | flex-wrap: wrap;
61 | gap: 24px;
62 | justify-content: center;
63 | @media (max-width: 750px) {
64 | gap: 14px;
65 | }
66 | `;
67 |
68 | const Item = styled.div`
69 | display: flex;
70 | flex-wrap: wrap;
71 | gap: 10px;
72 | `;
73 |
74 | const SelectableItem = styled.div`
75 | cursor: pointer;
76 | display: flex;
77 | border: 1px solid ${({ theme }) => theme.text_secondary + 50};
78 | color: ${({ theme }) => theme.text_secondary + 90};
79 | border-radius: 8px;
80 | padding: 2px 8px;
81 | font-size: 16px;
82 | width: fit-content;
83 | ${({ selected, theme }) =>
84 | selected &&
85 | `
86 | border: 1px solid ${theme.text_primary};
87 | color: ${theme.text_primary};
88 | background: ${theme.text_primary + 30};
89 | font-weight: 500;
90 | `}
91 | `;
92 |
93 | const ShopListing = () => {
94 | const [loading, setLoading] = useState(false);
95 | const [products, setProducts] = useState([]);
96 | const [priceRange, setPriceRange] = useState([0, 1000]);
97 | const [selectedSizes, setSelectedSizes] = useState(["S", "M", "L", "XL"]); // Default selected sizes
98 | const [selectedCategories, setSelectedCategories] = useState([
99 | "Men",
100 | "Women",
101 | "Kids",
102 | "Bags",
103 | ]); // Default selected categories
104 |
105 | const getFilteredProductsData = async () => {
106 | setLoading(true);
107 | // Call the API function for filtered products
108 | await getAllProducts(
109 | `minPrice=${priceRange[0]}&maxPrice=${priceRange[1]}${
110 | selectedSizes.length > 0 ? `&sizes=${selectedSizes.join(",")}` : ""
111 | }${
112 | selectedCategories.length > 0
113 | ? `&categories=${selectedCategories.join(",")}`
114 | : ""
115 | }`
116 | ).then((res) => {
117 | setProducts(res.data);
118 | setLoading(false);
119 | });
120 | };
121 |
122 | useEffect(() => {
123 | getFilteredProductsData();
124 | }, [priceRange, selectedSizes, selectedCategories]);
125 | return (
126 |
127 | {loading ? (
128 |
129 | ) : (
130 | <>
131 |
132 |
195 |
196 |
197 |
198 | {products?.map((product) => (
199 |
200 | ))}
201 |
202 |
203 | >
204 | )}
205 |
206 | );
207 | };
208 |
209 | export default ShopListing;
210 |
--------------------------------------------------------------------------------
/client/src/components/cards/ProductCard.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useNavigate } from "react-router-dom";
3 | import { CircularProgress, Rating } from "@mui/material";
4 | import styled from "styled-components";
5 | import {
6 | AddShoppingCartOutlined,
7 | FavoriteBorder,
8 | FavoriteRounded,
9 | } from "@mui/icons-material";
10 | import {
11 | addToCart,
12 | addToFavourite,
13 | deleteFromFavourite,
14 | getFavourite,
15 | } from "../../api";
16 | import { useDispatch } from "react-redux";
17 | import { openSnackbar } from "../../redux/reducers/snackbarSlice";
18 |
19 | const Card = styled.div`
20 | width: 250px;
21 | display: flex;
22 | flex-direction: column;
23 | gap: 16px;
24 | transition: all 0.3s ease-out;
25 | cursor: pointer;
26 | @media (max-width: 600px) {
27 | width: 170px;
28 | }
29 | `;
30 | const Image = styled.img`
31 | width: 100%;
32 | height: 320px;
33 | border-radius: 6px;
34 | object-fit: cover;
35 | transition: all 0.3s ease-out;
36 | @media (max-width: 600px) {
37 | height: 240px;
38 | }
39 | `;
40 | const Menu = styled.div`
41 | position: absolute;
42 | z-index: 10;
43 | color: ${({ theme }) => theme.text_primary};
44 | top: 14px;
45 | right: 14px;
46 | display: none;
47 | flex-direction: column;
48 | gap: 12px;
49 | `;
50 |
51 | const Top = styled.div`
52 | display: flex;
53 | align-items: center;
54 | justify-content: center;
55 | position: relative;
56 | border-radius: 6px;
57 | transition: all 0.3s ease-out;
58 | &:hover {
59 | background-color: ${({ theme }) => theme.primary};
60 | }
61 |
62 | &:hover ${Image} {
63 | opacity: 0.9;
64 | }
65 | &:hover ${Menu} {
66 | display: flex;
67 | }
68 | `;
69 | const MenuItem = styled.div`
70 | border-radius: 50%;
71 | width: 18px;
72 | height: 18px;
73 | background: white;
74 | padding: 8px;
75 | display: flex;
76 | align-items: center;
77 | justify-content: center;
78 | z-index: 200;
79 | `;
80 |
81 | const Rate = styled.div`
82 | position: absolute;
83 | z-index: 10;
84 | color: ${({ theme }) => theme.text_primary};
85 | bottom: 8px;
86 | left: 8px;
87 | padding: 4px 8px;
88 | border-radius: 4px;
89 | background: white;
90 | display: flex;
91 | align-items: center;
92 | opacity: 0.9;
93 | `;
94 |
95 | const Details = styled.div`
96 | import { useDispatch } from "react-redux";
97 | import { openSnackbar } from "../../redux/reducers/snackbarSlice";
98 | display: flex;
99 | gap: 6px;
100 | flex-direction: column;
101 | padding: 4px 10px;
102 | `;
103 | const Title = styled.div`
104 | font-size: 16px;
105 | font-weight: 700;
106 | color: ${({ theme }) => theme.text_primary};
107 | `;
108 | const Desc = styled.div`
109 | font-size: 16px;
110 | font-weight: 400;
111 | color: ${({ theme }) => theme.text_primary};
112 | overflow: hidden;
113 | text-overflow: ellipsis;
114 | white-space: nowrap;
115 | `;
116 | const Price = styled.div`
117 | display: flex;
118 | align-items: center;
119 | gap: 8px;
120 | font-size: 18px;
121 | font-weight: 500;
122 | color: ${({ theme }) => theme.text_primary};
123 | `;
124 | const Span = styled.div`
125 | font-size: 14px;
126 | font-weight: 500;
127 | color: ${({ theme }) => theme.text_secondary + 60};
128 | text-decoration: line-through;
129 | text-decoration-color: ${({ theme }) => theme.text_secondary + 50};
130 | `;
131 | const Percent = styled.div`
132 | font-size: 12px;
133 | font-weight: 500;
134 | color: green;
135 | `;
136 |
137 | const ProductCard = ({ product }) => {
138 | const dispatch = useDispatch();
139 | const navigate = useNavigate();
140 | const [favorite, setFavorite] = useState(false);
141 | const [favoriteLoading, setFavoriteLoading] = useState(false);
142 |
143 | const addFavorite = async () => {
144 | setFavoriteLoading(true);
145 | const token = localStorage.getItem("krist-app-token");
146 | await addToFavourite(token, { productID: product?._id })
147 | .then((res) => {
148 | setFavorite(true);
149 | setFavoriteLoading(false);
150 | })
151 | .catch((err) => {
152 | setFavoriteLoading(false);
153 | dispatch(
154 | openSnackbar({
155 | message: err.message,
156 | severity: "error",
157 | })
158 | );
159 | });
160 | };
161 | const removeFavorite = async () => {
162 | setFavoriteLoading(true);
163 | const token = localStorage.getItem("krist-app-token");
164 | await deleteFromFavourite(token, { productID: product?._id })
165 | .then((res) => {
166 | setFavorite(false);
167 | setFavoriteLoading(false);
168 | })
169 | .catch((err) => {
170 | setFavoriteLoading(false);
171 | dispatch(
172 | openSnackbar({
173 | message: err.message,
174 | severity: "error",
175 | })
176 | );
177 | });
178 | };
179 | const addCart = async () => {
180 | const token = localStorage.getItem("krist-app-token");
181 | await addToCart(token, { productId: product?._id, quantity: 1 })
182 | .then((res) => {
183 | navigate("/cart");
184 | })
185 | .catch((err) => {
186 | dispatch(
187 | openSnackbar({
188 | message: err.message,
189 | severity: "error",
190 | })
191 | );
192 | });
193 | };
194 | const checkFavourite = async () => {
195 | setFavoriteLoading(true);
196 | const token = localStorage.getItem("krist-app-token");
197 | await getFavourite(token, { productId: product?._id })
198 | .then((res) => {
199 | const isFavorite = res.data?.some(
200 | (favorite) => favorite._id === product?._id
201 | );
202 | setFavorite(isFavorite);
203 | setFavoriteLoading(false);
204 | })
205 | .catch((err) => {
206 | setFavoriteLoading(false);
207 | dispatch(
208 | openSnackbar({
209 | message: err.message,
210 | severity: "error",
211 | })
212 | );
213 | });
214 | };
215 |
216 | useEffect(() => {
217 | checkFavourite();
218 | }, []);
219 | return (
220 |
221 |
222 |
223 |
245 |
246 |
247 |
248 |
249 | navigate(`/shop/${product._id}`)}>
250 | {product?.title}
251 | {product?.name}
252 |
253 | ${product?.price?.org} ${product?.price?.mrp}
254 | ${product?.price?.off}% Off
255 |
256 |
257 |
258 | );
259 | };
260 |
261 | export default ProductCard;
262 |
--------------------------------------------------------------------------------
/client/src/pages/ProductDetails.jsx:
--------------------------------------------------------------------------------
1 | import { CircularProgress, Rating } from "@mui/material";
2 | import React, { useEffect, useState } from "react";
3 | import styled from "styled-components";
4 | import Button from "../components/Button";
5 | import { FavoriteBorder, FavoriteRounded } from "@mui/icons-material";
6 | import { useNavigate, useParams } from "react-router-dom";
7 | import { openSnackbar } from "../redux/reducers/snackbarSlice";
8 | import { useDispatch } from "react-redux";
9 | import {
10 | addToCart,
11 | addToFavourite,
12 | deleteFromFavourite,
13 | getFavourite,
14 | getProductDetails,
15 | } from "../api";
16 |
17 | const Container = styled.div`
18 | display: flex;
19 | justify-content: center;
20 | align-items: center;
21 | width: 100%;
22 | height: 99%;
23 | overflow-y: scroll;
24 | `;
25 | const Wrapper = styled.div`
26 | flex: 1;
27 | max-width: 1400px;
28 | display: flex;
29 | width: 100%;
30 | padding: 12px;
31 | gap: 32px;
32 | @media (max-width: 750px) {
33 | flex-direction: column;
34 | justify-content: center;
35 | }
36 | `;
37 | const ImageWrapper = styled.div`
38 | flex: 1;
39 | display: flex;
40 | align-items: center;
41 | justify-content: center;
42 | `;
43 | const Image = styled.img`
44 | height: 600px;
45 | border-radius: 12px;
46 | @media (max-width: 750px) {
47 | height: 400px;
48 | }
49 | `;
50 |
51 | const Details = styled.div`
52 | display: flex;
53 | gap: 18px;
54 | flex-direction: column;
55 | padding: 4px 10px;
56 | flex: 1;
57 | `;
58 |
59 | const Title = styled.div`
60 | font-size: 28px;
61 | font-weight: 600;
62 | color: ${({ theme }) => theme.text_primary};
63 | `;
64 | const Desc = styled.div`
65 | font-size: 16px;
66 | font-weight: 400;
67 | color: ${({ theme }) => theme.text_primary};
68 | `;
69 | const Name = styled.div`
70 | font-size: 18px;
71 | font-weight: 400;
72 | color: ${({ theme }) => theme.text_primary};
73 | `;
74 | const Price = styled.div`
75 | display: flex;
76 | align-items: center;
77 | gap: 8px;
78 | font-size: 22px;
79 | font-weight: 500;
80 | color: ${({ theme }) => theme.text_primary};
81 | `;
82 | const Span = styled.div`
83 | font-size: 16px;
84 | font-weight: 500;
85 | color: ${({ theme }) => theme.text_secondary + 60};
86 | text-decoration: line-through;
87 | text-decoration-color: ${({ theme }) => theme.text_secondary + 50};
88 | `;
89 | const Percent = styled.div`
90 | font-size: 16px;
91 | font-weight: 500;
92 | color: green;
93 | `;
94 |
95 | const Sizes = styled.div`
96 | font-size: 18px;
97 | font-weight: 500;
98 | display: flex;
99 | flex-direction: column;
100 | gap: 12px;
101 | `;
102 | const Items = styled.div`
103 | display: flex;
104 | gap: 12px;
105 | `;
106 | const Item = styled.div`
107 | border: 1px solid ${({ theme }) => theme.primary};
108 | font-size: 14px;
109 | width: 42px;
110 | height: 42px;
111 | border-radius: 50%;
112 | display: flex;
113 | align-items: center;
114 | justify-content: center;
115 | ${({ selected, theme }) =>
116 | selected &&
117 | `
118 | background: ${theme.primary};
119 | color: white;
120 | `}
121 | `;
122 | const ButtonWrapper = styled.div`
123 | display: flex;
124 | gap: 16px;
125 | padding: 32px 0px;
126 | `;
127 | const ProductDetails = () => {
128 | const { id } = useParams();
129 | const dispatch = useDispatch();
130 | const navigate = useNavigate();
131 | const [loading, setLoading] = useState(false);
132 | const [product, setProduct] = useState();
133 | const [selected, setSelected] = useState();
134 | const [favorite, setFavorite] = useState(false);
135 | const [favoriteLoading, setFavoriteLoading] = useState(false);
136 | const [cartLoading, setCartLoading] = useState(false);
137 |
138 | const getProduct = async () => {
139 | setLoading(true);
140 | await getProductDetails(id).then((res) => {
141 | setProduct(res.data);
142 | setLoading(false);
143 | });
144 | };
145 |
146 | const addFavorite = async () => {
147 | setFavoriteLoading(true);
148 | const token = localStorage.getItem("krist-app-token");
149 | await addToFavourite(token, { productID: product?._id })
150 | .then((res) => {
151 | setFavorite(true);
152 | setFavoriteLoading(false);
153 | })
154 | .catch((err) => {
155 | setFavoriteLoading(false);
156 | dispatch(
157 | openSnackbar({
158 | message: err.message,
159 | severity: "error",
160 | })
161 | );
162 | });
163 | };
164 | const removeFavorite = async () => {
165 | setFavoriteLoading(true);
166 | const token = localStorage.getItem("krist-app-token");
167 | await deleteFromFavourite(token, { productID: product?._id })
168 | .then((res) => {
169 | setFavorite(false);
170 | setFavoriteLoading(false);
171 | })
172 | .catch((err) => {
173 | setFavoriteLoading(false);
174 | dispatch(
175 | openSnackbar({
176 | message: err.message,
177 | severity: "error",
178 | })
179 | );
180 | });
181 | };
182 | const addCart = async () => {
183 | setCartLoading(true);
184 | const token = localStorage.getItem("krist-app-token");
185 | await addToCart(token, { productId: product?._id, quantity: 1 })
186 | .then((res) => {
187 | setCartLoading(false);
188 | navigate("/cart");
189 | })
190 | .catch((err) => {
191 | setCartLoading(false);
192 | dispatch(
193 | openSnackbar({
194 | message: err.message,
195 | severity: "error",
196 | })
197 | );
198 | });
199 | };
200 | const checkFavourite = async () => {
201 | setFavoriteLoading(true);
202 | const token = localStorage.getItem("krist-app-token");
203 | await getFavourite(token, { productId: product?._id })
204 | .then((res) => {
205 | const isFavorite = res.data?.some(
206 | (favorite) => favorite._id === product?._id
207 | );
208 | setFavorite(isFavorite);
209 | setFavoriteLoading(false);
210 | })
211 | .catch((err) => {
212 | setFavoriteLoading(false);
213 | dispatch(
214 | openSnackbar({
215 | message: err.message,
216 | severity: "error",
217 | })
218 | );
219 | });
220 | };
221 |
222 | useEffect(() => {
223 | getProduct();
224 | checkFavourite();
225 | }, []);
226 |
227 | return (
228 |
229 | {loading ? (
230 |
231 | ) : (
232 |
233 |
234 |
235 |
236 |
237 |
238 |
{product?.title}
239 | {product?.name}
240 |
241 |
242 |
243 | ${product?.price?.org} ${product?.price?.mrp}{" "}
244 | (${product?.price?.off}% Off)
245 |
246 | {product?.desc}
247 |
248 |
249 | {product?.sizes.map((size) => (
250 | - setSelected(size)}
253 | >
254 | {size}
255 |
256 | ))}
257 |
258 |
259 |
260 |
282 |
283 |
284 | )}
285 |
286 | );
287 | };
288 |
289 | export default ProductDetails;
290 |
--------------------------------------------------------------------------------
/client/src/components/Navbar.jsx:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import styled from "styled-components";
3 | import LogoImg from "../utils/Images/Logo.png";
4 | import { NavLink } from "react-router-dom";
5 | import Button from "./Button";
6 | import {
7 | FavoriteBorder,
8 | MenuRounded,
9 | SearchRounded,
10 | ShoppingCartOutlined,
11 | } from "@mui/icons-material";
12 | import { Avatar } from "@mui/material";
13 | import { logout } from "../redux/reducers/userSlice";
14 | import { useDispatch } from "react-redux";
15 |
16 | const Nav = styled.div`
17 | background-color: ${({ theme }) => theme.bg};
18 | height: 80px;
19 | display: flex;
20 | align-items: center;
21 | justify-content: center;
22 | font-size: 1rem;
23 | position: sticky;
24 | top: 0;
25 | z-index: 10;
26 | color: white;
27 | `;
28 | const NavbarContainer = styled.div`
29 | width: 100%;
30 | max-width: 1400px;
31 | padding: 0 24px;
32 | display: flex;
33 | gap: 14px;
34 | align-items: center;
35 | justify-content: space-between;
36 | font-size: 1rem;
37 | `;
38 | const NavLogo = styled.div`
39 | width: 100%;
40 | display: flex;
41 | align-items: center;
42 | padding: 0 6px;
43 | font-weight: 500;
44 | font-size: 18px;
45 | text-decoration: none;
46 | color: inherit;
47 | `;
48 | const Logo = styled.img`
49 | height: 34px;
50 | `;
51 | const NavItems = styled.ul`
52 | width: 100%;
53 | display: flex;
54 | align-items: center;
55 | justify-content: center;
56 | gap: 32px;
57 | padding: 0 6px;
58 | list-style: none;
59 | @media screen and (max-width: 768px) {
60 | display: none;
61 | }
62 | `;
63 | const Navlink = styled(NavLink)`
64 | display: flex;
65 | align-items: center;
66 | color: ${({ theme }) => theme.text_primary};
67 | font-weight: 500;
68 | cursor: pointer;
69 | transition: all 1s slide-in;
70 | text-decoration: none;
71 | &:hover {
72 | color: ${({ theme }) => theme.primary};
73 | }
74 | &.active {
75 | color: ${({ theme }) => theme.primary};
76 | border-bottom: 1.8px solid ${({ theme }) => theme.primary};
77 | }
78 | `;
79 |
80 | const ButtonContainer = styled.div`
81 | width: 100%;
82 | height: 100%;
83 | display: flex;
84 | justify-content: flex-end;
85 | gap: 28px;
86 | align-items: center;
87 | padding: 0 6px;
88 | color: ${({ theme }) => theme.primary};
89 | @media screen and (max-width: 768px) {
90 | display: none;
91 | }
92 | `;
93 |
94 | const MobileIcon = styled.div`
95 | color: ${({ theme }) => theme.text_primary};
96 | display: none;
97 | @media screen and (max-width: 768px) {
98 | display: flex;
99 | align-items: center;
100 | }
101 | `;
102 | const Mobileicons = styled.div`
103 | color: ${({ theme }) => theme.text_primary};
104 | display: none;
105 | @media screen and (max-width: 768px) {
106 | display: flex;
107 | align-items: center;
108 | justify-content: center;
109 | gap: 16px;
110 | }
111 | `;
112 |
113 | const MobileMenu = styled.ul`
114 | display: flex;
115 | flex-direction: column;
116 | align-items: start;
117 | gap: 16px;
118 | padding: 0 6px;
119 | list-style: none;
120 | width: 80%;
121 | padding: 12px 40px 24px 40px;
122 | background: ${({ theme }) => theme.card_light + 99};
123 | position: absolute;
124 | top: 80px;
125 | right: 0;
126 | transition: all 0.6s ease-in-out;
127 | transform: ${({ isOpen }) =>
128 | isOpen ? "translateY(0)" : "translateY(-100%)"};
129 | border-radius: 0 0 20px 20px;
130 | box-shadow: 0 0 10px 0 rgba(0, 0, 0, 0.2);
131 | opacity: ${({ isOpen }) => (isOpen ? "100%" : "0")};
132 | z-index: ${({ isOpen }) => (isOpen ? "1000" : "-1000")};
133 | `;
134 | const TextButton = styled.div`
135 | text-align: end;
136 | color: ${({ theme }) => theme.secondary};
137 | cursor: pointer;
138 | font-size: 16px;
139 | transition: all 0.3s ease;
140 | font-weight: 600;
141 | &:hover {
142 | color: ${({ theme }) => theme.primary};
143 | }
144 | `;
145 |
146 | const Navbar = ({ openAuth, setOpenAuth, currentUser }) => {
147 | const [isOpen, setIsOpen] = useState(false);
148 | const dispatch = useDispatch();
149 | return (
150 |
281 | );
282 | };
283 |
284 | export default Navbar;
285 |
--------------------------------------------------------------------------------
/client/src/pages/Cart.jsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import styled from "styled-components";
3 | import TextInput from "../components/TextInput";
4 | import Button from "../components/Button";
5 | import { addToCart, deleteFromCart, getCart, placeOrder } from "../api";
6 | import { useNavigate } from "react-router-dom";
7 | import { CircularProgress } from "@mui/material";
8 | import { useDispatch } from "react-redux";
9 | import { openSnackbar } from "../redux/reducers/snackbarSlice";
10 | import { DeleteOutline } from "@mui/icons-material";
11 |
12 | const Container = styled.div`
13 | padding: 20px 30px;
14 | padding-bottom: 200px;
15 | height: 100%;
16 | overflow-y: scroll;
17 | display: flex;
18 | align-items: center;
19 | flex-direction: column;
20 | gap: 30px;
21 | @media (max-width: 768px) {
22 | padding: 20px 12px;
23 | }
24 | background: ${({ theme }) => theme.bg};
25 | `;
26 | const Section = styled.div`
27 | width: 100%;
28 | max-width: 1400px;
29 | padding: 32px 16px;
30 | display: flex;
31 | flex-direction: column;
32 | align-items: center;
33 | font-size: 22px;
34 | gap: 28px;
35 | `;
36 | const Title = styled.div`
37 | font-size: 28px;
38 | font-weight: 500;
39 | display: flex;
40 | justify-content: ${({ center }) => (center ? "center" : "space-between")};
41 | align-items: center;
42 | `;
43 |
44 | const Wrapper = styled.div`
45 | display: flex;
46 | gap: 32px;
47 | width: 100%;
48 | padding: 12px;
49 | @media (max-width: 750px) {
50 | flex-direction: column;
51 | }
52 | `;
53 | const Left = styled.div`
54 | flex: 1;
55 | display: flex;
56 | flex-direction: column;
57 | gap: 12px;
58 | @media (max-width: 750px) {
59 | flex: 1.2;
60 | }
61 | `;
62 | const Table = styled.div`
63 | font-size: 16px;
64 | display: flex;
65 | align-items: center;
66 | gap: 30px;
67 | ${({ head }) => head && `margin-bottom: 22px`}
68 | `;
69 | const TableItem = styled.div`
70 | ${({ flex }) => flex && `flex: 1; `}
71 | ${({ bold }) =>
72 | bold &&
73 | `font-weight: 600;
74 | font-size: 18px;`}
75 | `;
76 | const Counter = styled.div`
77 | display: flex;
78 | gap: 12px;
79 | align-items: center;
80 | border: 1px solid ${({ theme }) => theme.text_secondary + 40};
81 | border-radius: 8px;
82 | padding: 4px 12px;
83 | `;
84 |
85 | const Product = styled.div`
86 | display: flex;
87 | gap: 16px;
88 | `;
89 | const Img = styled.img`
90 | height: 80px;
91 | `;
92 | const Details = styled.div``;
93 | const Protitle = styled.div`
94 | color: ${({ theme }) => theme.primary};
95 | font-size: 16px;
96 | font-weight: 500;
97 | `;
98 | const ProDesc = styled.div`
99 | font-size: 14px;
100 | font-weight: 400;
101 | color: ${({ theme }) => theme.text_primary};
102 | overflow: hidden;
103 | text-overflow: ellipsis;
104 | white-space: nowrap;
105 | `;
106 | const ProSize = styled.div`
107 | font-size: 14px;
108 | font-weight: 500;
109 | `;
110 |
111 | const Right = styled.div`
112 | flex: 1;
113 | display: flex;
114 | flex-direction: column;
115 | gap: 12px;
116 | @media (max-width: 750px) {
117 | flex: 0.8;
118 | }
119 | `;
120 | const Subtotal = styled.div`
121 | font-size: 22px;
122 | font-weight: 600;
123 | display: flex;
124 | justify-content: space-between;
125 | `;
126 | const Delivery = styled.div`
127 | font-size: 18px;
128 | font-weight: 500;
129 | display: flex;
130 | gap: 6px;
131 | flex-direction: column;
132 | `;
133 |
134 | const Cart = () => {
135 | const navigate = useNavigate();
136 | const dispatch = useDispatch();
137 | const [loading, setLoading] = useState(false);
138 | const [reload, setReload] = useState(false);
139 | const [products, setProducts] = useState([]);
140 | const [buttonLoad, setButtonLoad] = useState(false);
141 |
142 | const [deliveryDetails, setDeliveryDetails] = useState({
143 | firstName: "",
144 | lastName: "",
145 | emailAddress: "",
146 | phoneNumber: "",
147 | completeAddress: "",
148 | });
149 |
150 | const getProducts = async () => {
151 | setLoading(true);
152 | const token = localStorage.getItem("krist-app-token");
153 | await getCart(token).then((res) => {
154 | setProducts(res.data);
155 | setLoading(false);
156 | });
157 | };
158 |
159 | const addCart = async (id) => {
160 | const token = localStorage.getItem("krist-app-token");
161 | await addToCart(token, { productId: id, quantity: 1 })
162 | .then((res) => {
163 | setReload(!reload);
164 | })
165 | .catch((err) => {
166 | setReload(!reload);
167 | dispatch(
168 | openSnackbar({
169 | message: err.message,
170 | severity: "error",
171 | })
172 | );
173 | });
174 | };
175 |
176 | const removeCart = async (id, quantity, type) => {
177 | const token = localStorage.getItem("krist-app-token");
178 | let qnt = quantity > 0 ? 1 : null;
179 | if (type === "full") qnt = null;
180 | await deleteFromCart(token, {
181 | productId: id,
182 | quantity: qnt,
183 | })
184 | .then((res) => {
185 | setReload(!reload);
186 | })
187 | .catch((err) => {
188 | setReload(!reload);
189 | dispatch(
190 | openSnackbar({
191 | message: err.message,
192 | severity: "error",
193 | })
194 | );
195 | });
196 | };
197 |
198 | const calculateSubtotal = () => {
199 | return products.reduce(
200 | (total, item) => total + item.quantity * item?.product?.price?.org,
201 | 0
202 | );
203 | };
204 |
205 | useEffect(() => {
206 | getProducts();
207 | }, [reload]);
208 |
209 | const convertAddressToString = (addressObj) => {
210 | // Convert the address object to a string representation
211 | return `${addressObj.firstName} ${addressObj.lastName}, ${addressObj.completeAddress}, ${addressObj.phoneNumber}, ${addressObj.emailAddress}`;
212 | };
213 |
214 | const PlaceOrder = async () => {
215 | setButtonLoad(true);
216 | try {
217 | const isDeliveryDetailsFilled =
218 | deliveryDetails.firstName &&
219 | deliveryDetails.lastName &&
220 | deliveryDetails.completeAddress &&
221 | deliveryDetails.phoneNumber &&
222 | deliveryDetails.emailAddress;
223 |
224 | if (!isDeliveryDetailsFilled) {
225 | // Show an error message or handle the situation where delivery details are incomplete
226 | dispatch(
227 | openSnackbar({
228 | message: "Please fill in all required delivery details.",
229 | severity: "error",
230 | })
231 | );
232 | return;
233 | }
234 | const token = localStorage.getItem("krist-app-token");
235 | const totalAmount = calculateSubtotal().toFixed(2);
236 | const orderDetails = {
237 | products,
238 | address: convertAddressToString(deliveryDetails),
239 | totalAmount,
240 | };
241 |
242 | await placeOrder(token, orderDetails);
243 |
244 | // Show success message or navigate to a success page
245 | dispatch(
246 | openSnackbar({
247 | message: "Order placed successfully",
248 | severity: "success",
249 | })
250 | );
251 | setButtonLoad(false);
252 | // Clear the cart and update the UI
253 | setReload(!reload);
254 | } catch (error) {
255 | // Handle errors, show error message, etc.
256 | dispatch(
257 | openSnackbar({
258 | message: "Failed to place order. Please try again.",
259 | severity: "error",
260 | })
261 | );
262 | setButtonLoad(false);
263 | }
264 | };
265 | return (
266 |
267 | {loading ? (
268 |
269 | ) : (
270 |
271 | Your Shopping Cart
272 | {products.length === 0 ? (
273 | <>Cart is empty>
274 | ) : (
275 |
276 |
277 |
278 |
279 | Product
280 |
281 | Price
282 | Quantity
283 | Subtotal
284 |
285 |
286 | {products?.map((item) => (
287 |
288 |
289 |
290 |
291 |
292 | {item?.product?.title}
293 | {item?.product?.name}
294 | Size: Xl
295 |
296 |
297 |
298 | ${item?.product?.price?.org}
299 |
300 |
301 |
307 | removeCart(item?.product?._id, item?.quantity - 1)
308 | }
309 | >
310 | -
311 |
312 | {item?.quantity}
313 | addCart(item?.product?._id)}
319 | >
320 | +
321 |
322 |
323 |
324 |
325 | {" "}
326 | ${(item.quantity * item?.product?.price?.org).toFixed(2)}
327 |
328 |
329 |
332 | removeCart(
333 | item?.product?._id,
334 | item?.quantity - 1,
335 | "full"
336 | )
337 | }
338 | />
339 |
340 |
341 | ))}
342 |
343 |
344 |
345 | Subtotal : ${calculateSubtotal().toFixed(2)}
346 |
347 |
348 | Delivery Details:
349 |
350 |
356 |
361 | setDeliveryDetails({
362 | ...deliveryDetails,
363 | firstName: e.target.value,
364 | })
365 | }
366 | />
367 |
372 | setDeliveryDetails({
373 | ...deliveryDetails,
374 | lastName: e.target.value,
375 | })
376 | }
377 | />
378 |
379 |
383 | setDeliveryDetails({
384 | ...deliveryDetails,
385 | emailAddress: e.target.value,
386 | })
387 | }
388 | placeholder="Email Address"
389 | />
390 |
394 | setDeliveryDetails({
395 | ...deliveryDetails,
396 | phoneNumber: e.target.value,
397 | })
398 | }
399 | placeholder="Phone no. +91 XXXXX XXXXX"
400 | />
401 |
406 | setDeliveryDetails({
407 | ...deliveryDetails,
408 | completeAddress: e.target.value,
409 | })
410 | }
411 | value={deliveryDetails.completeAddress}
412 | placeholder="Complete Address (Address, State, Country, Pincode)"
413 | />
414 |
415 |
416 |
417 | Payment Details:
418 |
419 |
420 |
426 |
427 |
428 |
429 |
430 |
431 |
432 |
439 |
440 |
441 | )}
442 |
443 | )}
444 |
445 | );
446 | };
447 |
448 | export default Cart;
449 |
--------------------------------------------------------------------------------
/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 | "cors": "^2.8.5",
14 | "dotenv": "^16.4.5",
15 | "express": "^4.18.3",
16 | "jsonwebtoken": "^9.0.2",
17 | "mongoose": "^8.2.2",
18 | "nodemon": "^3.1.0"
19 | }
20 | },
21 | "node_modules/@mapbox/node-pre-gyp": {
22 | "version": "1.0.11",
23 | "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
24 | "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==",
25 | "dependencies": {
26 | "detect-libc": "^2.0.0",
27 | "https-proxy-agent": "^5.0.0",
28 | "make-dir": "^3.1.0",
29 | "node-fetch": "^2.6.7",
30 | "nopt": "^5.0.0",
31 | "npmlog": "^5.0.1",
32 | "rimraf": "^3.0.2",
33 | "semver": "^7.3.5",
34 | "tar": "^6.1.11"
35 | },
36 | "bin": {
37 | "node-pre-gyp": "bin/node-pre-gyp"
38 | }
39 | },
40 | "node_modules/@mongodb-js/saslprep": {
41 | "version": "1.1.5",
42 | "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.5.tgz",
43 | "integrity": "sha512-XLNOMH66KhJzUJNwT/qlMnS4WsNDWD5ASdyaSH3EtK+F4r/CFGa3jT4GNi4mfOitGvWXtdLgQJkQjxSVrio+jA==",
44 | "dependencies": {
45 | "sparse-bitfield": "^3.0.3"
46 | }
47 | },
48 | "node_modules/@types/webidl-conversions": {
49 | "version": "7.0.3",
50 | "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
51 | "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
52 | },
53 | "node_modules/@types/whatwg-url": {
54 | "version": "11.0.4",
55 | "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz",
56 | "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==",
57 | "dependencies": {
58 | "@types/webidl-conversions": "*"
59 | }
60 | },
61 | "node_modules/abbrev": {
62 | "version": "1.1.1",
63 | "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
64 | "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
65 | },
66 | "node_modules/accepts": {
67 | "version": "1.3.8",
68 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
69 | "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
70 | "dependencies": {
71 | "mime-types": "~2.1.34",
72 | "negotiator": "0.6.3"
73 | },
74 | "engines": {
75 | "node": ">= 0.6"
76 | }
77 | },
78 | "node_modules/agent-base": {
79 | "version": "6.0.2",
80 | "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
81 | "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
82 | "dependencies": {
83 | "debug": "4"
84 | },
85 | "engines": {
86 | "node": ">= 6.0.0"
87 | }
88 | },
89 | "node_modules/agent-base/node_modules/debug": {
90 | "version": "4.3.4",
91 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
92 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
93 | "dependencies": {
94 | "ms": "2.1.2"
95 | },
96 | "engines": {
97 | "node": ">=6.0"
98 | },
99 | "peerDependenciesMeta": {
100 | "supports-color": {
101 | "optional": true
102 | }
103 | }
104 | },
105 | "node_modules/agent-base/node_modules/ms": {
106 | "version": "2.1.2",
107 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
108 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
109 | },
110 | "node_modules/ansi-regex": {
111 | "version": "5.0.1",
112 | "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
113 | "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
114 | "engines": {
115 | "node": ">=8"
116 | }
117 | },
118 | "node_modules/anymatch": {
119 | "version": "3.1.3",
120 | "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
121 | "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
122 | "dependencies": {
123 | "normalize-path": "^3.0.0",
124 | "picomatch": "^2.0.4"
125 | },
126 | "engines": {
127 | "node": ">= 8"
128 | }
129 | },
130 | "node_modules/aproba": {
131 | "version": "2.0.0",
132 | "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
133 | "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="
134 | },
135 | "node_modules/are-we-there-yet": {
136 | "version": "2.0.0",
137 | "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz",
138 | "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==",
139 | "dependencies": {
140 | "delegates": "^1.0.0",
141 | "readable-stream": "^3.6.0"
142 | },
143 | "engines": {
144 | "node": ">=10"
145 | }
146 | },
147 | "node_modules/array-flatten": {
148 | "version": "1.1.1",
149 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
150 | "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
151 | },
152 | "node_modules/balanced-match": {
153 | "version": "1.0.2",
154 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
155 | "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
156 | },
157 | "node_modules/bcrypt": {
158 | "version": "5.1.1",
159 | "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz",
160 | "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==",
161 | "hasInstallScript": true,
162 | "dependencies": {
163 | "@mapbox/node-pre-gyp": "^1.0.11",
164 | "node-addon-api": "^5.0.0"
165 | },
166 | "engines": {
167 | "node": ">= 10.0.0"
168 | }
169 | },
170 | "node_modules/binary-extensions": {
171 | "version": "2.3.0",
172 | "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
173 | "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
174 | "engines": {
175 | "node": ">=8"
176 | },
177 | "funding": {
178 | "url": "https://github.com/sponsors/sindresorhus"
179 | }
180 | },
181 | "node_modules/body-parser": {
182 | "version": "1.20.2",
183 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
184 | "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
185 | "dependencies": {
186 | "bytes": "3.1.2",
187 | "content-type": "~1.0.5",
188 | "debug": "2.6.9",
189 | "depd": "2.0.0",
190 | "destroy": "1.2.0",
191 | "http-errors": "2.0.0",
192 | "iconv-lite": "0.4.24",
193 | "on-finished": "2.4.1",
194 | "qs": "6.11.0",
195 | "raw-body": "2.5.2",
196 | "type-is": "~1.6.18",
197 | "unpipe": "1.0.0"
198 | },
199 | "engines": {
200 | "node": ">= 0.8",
201 | "npm": "1.2.8000 || >= 1.4.16"
202 | }
203 | },
204 | "node_modules/brace-expansion": {
205 | "version": "1.1.11",
206 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
207 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
208 | "dependencies": {
209 | "balanced-match": "^1.0.0",
210 | "concat-map": "0.0.1"
211 | }
212 | },
213 | "node_modules/braces": {
214 | "version": "3.0.2",
215 | "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
216 | "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
217 | "dependencies": {
218 | "fill-range": "^7.0.1"
219 | },
220 | "engines": {
221 | "node": ">=8"
222 | }
223 | },
224 | "node_modules/bson": {
225 | "version": "6.5.0",
226 | "resolved": "https://registry.npmjs.org/bson/-/bson-6.5.0.tgz",
227 | "integrity": "sha512-DXf1BTAS8vKyR90BO4x5v3rKVarmkdkzwOrnYDFdjAY694ILNDkmA3uRh1xXJEl+C1DAh8XCvAQ+Gh3kzubtpg==",
228 | "engines": {
229 | "node": ">=16.20.1"
230 | }
231 | },
232 | "node_modules/buffer-equal-constant-time": {
233 | "version": "1.0.1",
234 | "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
235 | "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
236 | },
237 | "node_modules/bytes": {
238 | "version": "3.1.2",
239 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
240 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
241 | "engines": {
242 | "node": ">= 0.8"
243 | }
244 | },
245 | "node_modules/call-bind": {
246 | "version": "1.0.7",
247 | "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
248 | "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
249 | "dependencies": {
250 | "es-define-property": "^1.0.0",
251 | "es-errors": "^1.3.0",
252 | "function-bind": "^1.1.2",
253 | "get-intrinsic": "^1.2.4",
254 | "set-function-length": "^1.2.1"
255 | },
256 | "engines": {
257 | "node": ">= 0.4"
258 | },
259 | "funding": {
260 | "url": "https://github.com/sponsors/ljharb"
261 | }
262 | },
263 | "node_modules/chokidar": {
264 | "version": "3.6.0",
265 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
266 | "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
267 | "dependencies": {
268 | "anymatch": "~3.1.2",
269 | "braces": "~3.0.2",
270 | "glob-parent": "~5.1.2",
271 | "is-binary-path": "~2.1.0",
272 | "is-glob": "~4.0.1",
273 | "normalize-path": "~3.0.0",
274 | "readdirp": "~3.6.0"
275 | },
276 | "engines": {
277 | "node": ">= 8.10.0"
278 | },
279 | "funding": {
280 | "url": "https://paulmillr.com/funding/"
281 | },
282 | "optionalDependencies": {
283 | "fsevents": "~2.3.2"
284 | }
285 | },
286 | "node_modules/chownr": {
287 | "version": "2.0.0",
288 | "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
289 | "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
290 | "engines": {
291 | "node": ">=10"
292 | }
293 | },
294 | "node_modules/color-support": {
295 | "version": "1.1.3",
296 | "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz",
297 | "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==",
298 | "bin": {
299 | "color-support": "bin.js"
300 | }
301 | },
302 | "node_modules/concat-map": {
303 | "version": "0.0.1",
304 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
305 | "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
306 | },
307 | "node_modules/console-control-strings": {
308 | "version": "1.1.0",
309 | "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
310 | "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="
311 | },
312 | "node_modules/content-disposition": {
313 | "version": "0.5.4",
314 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
315 | "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
316 | "dependencies": {
317 | "safe-buffer": "5.2.1"
318 | },
319 | "engines": {
320 | "node": ">= 0.6"
321 | }
322 | },
323 | "node_modules/content-type": {
324 | "version": "1.0.5",
325 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
326 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
327 | "engines": {
328 | "node": ">= 0.6"
329 | }
330 | },
331 | "node_modules/cookie": {
332 | "version": "0.5.0",
333 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
334 | "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
335 | "engines": {
336 | "node": ">= 0.6"
337 | }
338 | },
339 | "node_modules/cookie-signature": {
340 | "version": "1.0.6",
341 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
342 | "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
343 | },
344 | "node_modules/cors": {
345 | "version": "2.8.5",
346 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
347 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
348 | "dependencies": {
349 | "object-assign": "^4",
350 | "vary": "^1"
351 | },
352 | "engines": {
353 | "node": ">= 0.10"
354 | }
355 | },
356 | "node_modules/debug": {
357 | "version": "2.6.9",
358 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
359 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
360 | "dependencies": {
361 | "ms": "2.0.0"
362 | }
363 | },
364 | "node_modules/define-data-property": {
365 | "version": "1.1.4",
366 | "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
367 | "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
368 | "dependencies": {
369 | "es-define-property": "^1.0.0",
370 | "es-errors": "^1.3.0",
371 | "gopd": "^1.0.1"
372 | },
373 | "engines": {
374 | "node": ">= 0.4"
375 | },
376 | "funding": {
377 | "url": "https://github.com/sponsors/ljharb"
378 | }
379 | },
380 | "node_modules/delegates": {
381 | "version": "1.0.0",
382 | "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
383 | "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="
384 | },
385 | "node_modules/depd": {
386 | "version": "2.0.0",
387 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
388 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
389 | "engines": {
390 | "node": ">= 0.8"
391 | }
392 | },
393 | "node_modules/destroy": {
394 | "version": "1.2.0",
395 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
396 | "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
397 | "engines": {
398 | "node": ">= 0.8",
399 | "npm": "1.2.8000 || >= 1.4.16"
400 | }
401 | },
402 | "node_modules/detect-libc": {
403 | "version": "2.0.2",
404 | "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz",
405 | "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==",
406 | "engines": {
407 | "node": ">=8"
408 | }
409 | },
410 | "node_modules/dotenv": {
411 | "version": "16.4.5",
412 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz",
413 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==",
414 | "engines": {
415 | "node": ">=12"
416 | },
417 | "funding": {
418 | "url": "https://dotenvx.com"
419 | }
420 | },
421 | "node_modules/ecdsa-sig-formatter": {
422 | "version": "1.0.11",
423 | "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
424 | "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
425 | "dependencies": {
426 | "safe-buffer": "^5.0.1"
427 | }
428 | },
429 | "node_modules/ee-first": {
430 | "version": "1.1.1",
431 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
432 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
433 | },
434 | "node_modules/emoji-regex": {
435 | "version": "8.0.0",
436 | "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
437 | "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
438 | },
439 | "node_modules/encodeurl": {
440 | "version": "1.0.2",
441 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
442 | "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
443 | "engines": {
444 | "node": ">= 0.8"
445 | }
446 | },
447 | "node_modules/es-define-property": {
448 | "version": "1.0.0",
449 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
450 | "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
451 | "dependencies": {
452 | "get-intrinsic": "^1.2.4"
453 | },
454 | "engines": {
455 | "node": ">= 0.4"
456 | }
457 | },
458 | "node_modules/es-errors": {
459 | "version": "1.3.0",
460 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
461 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
462 | "engines": {
463 | "node": ">= 0.4"
464 | }
465 | },
466 | "node_modules/escape-html": {
467 | "version": "1.0.3",
468 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
469 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
470 | },
471 | "node_modules/etag": {
472 | "version": "1.8.1",
473 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
474 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
475 | "engines": {
476 | "node": ">= 0.6"
477 | }
478 | },
479 | "node_modules/express": {
480 | "version": "4.18.3",
481 | "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz",
482 | "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==",
483 | "dependencies": {
484 | "accepts": "~1.3.8",
485 | "array-flatten": "1.1.1",
486 | "body-parser": "1.20.2",
487 | "content-disposition": "0.5.4",
488 | "content-type": "~1.0.4",
489 | "cookie": "0.5.0",
490 | "cookie-signature": "1.0.6",
491 | "debug": "2.6.9",
492 | "depd": "2.0.0",
493 | "encodeurl": "~1.0.2",
494 | "escape-html": "~1.0.3",
495 | "etag": "~1.8.1",
496 | "finalhandler": "1.2.0",
497 | "fresh": "0.5.2",
498 | "http-errors": "2.0.0",
499 | "merge-descriptors": "1.0.1",
500 | "methods": "~1.1.2",
501 | "on-finished": "2.4.1",
502 | "parseurl": "~1.3.3",
503 | "path-to-regexp": "0.1.7",
504 | "proxy-addr": "~2.0.7",
505 | "qs": "6.11.0",
506 | "range-parser": "~1.2.1",
507 | "safe-buffer": "5.2.1",
508 | "send": "0.18.0",
509 | "serve-static": "1.15.0",
510 | "setprototypeof": "1.2.0",
511 | "statuses": "2.0.1",
512 | "type-is": "~1.6.18",
513 | "utils-merge": "1.0.1",
514 | "vary": "~1.1.2"
515 | },
516 | "engines": {
517 | "node": ">= 0.10.0"
518 | }
519 | },
520 | "node_modules/fill-range": {
521 | "version": "7.0.1",
522 | "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
523 | "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
524 | "dependencies": {
525 | "to-regex-range": "^5.0.1"
526 | },
527 | "engines": {
528 | "node": ">=8"
529 | }
530 | },
531 | "node_modules/finalhandler": {
532 | "version": "1.2.0",
533 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
534 | "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
535 | "dependencies": {
536 | "debug": "2.6.9",
537 | "encodeurl": "~1.0.2",
538 | "escape-html": "~1.0.3",
539 | "on-finished": "2.4.1",
540 | "parseurl": "~1.3.3",
541 | "statuses": "2.0.1",
542 | "unpipe": "~1.0.0"
543 | },
544 | "engines": {
545 | "node": ">= 0.8"
546 | }
547 | },
548 | "node_modules/forwarded": {
549 | "version": "0.2.0",
550 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
551 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
552 | "engines": {
553 | "node": ">= 0.6"
554 | }
555 | },
556 | "node_modules/fresh": {
557 | "version": "0.5.2",
558 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
559 | "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
560 | "engines": {
561 | "node": ">= 0.6"
562 | }
563 | },
564 | "node_modules/fs-minipass": {
565 | "version": "2.1.0",
566 | "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
567 | "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
568 | "dependencies": {
569 | "minipass": "^3.0.0"
570 | },
571 | "engines": {
572 | "node": ">= 8"
573 | }
574 | },
575 | "node_modules/fs-minipass/node_modules/minipass": {
576 | "version": "3.3.6",
577 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
578 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
579 | "dependencies": {
580 | "yallist": "^4.0.0"
581 | },
582 | "engines": {
583 | "node": ">=8"
584 | }
585 | },
586 | "node_modules/fs.realpath": {
587 | "version": "1.0.0",
588 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
589 | "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
590 | },
591 | "node_modules/fsevents": {
592 | "version": "2.3.3",
593 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
594 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
595 | "hasInstallScript": true,
596 | "optional": true,
597 | "os": [
598 | "darwin"
599 | ],
600 | "engines": {
601 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
602 | }
603 | },
604 | "node_modules/function-bind": {
605 | "version": "1.1.2",
606 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
607 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
608 | "funding": {
609 | "url": "https://github.com/sponsors/ljharb"
610 | }
611 | },
612 | "node_modules/gauge": {
613 | "version": "3.0.2",
614 | "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz",
615 | "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==",
616 | "dependencies": {
617 | "aproba": "^1.0.3 || ^2.0.0",
618 | "color-support": "^1.1.2",
619 | "console-control-strings": "^1.0.0",
620 | "has-unicode": "^2.0.1",
621 | "object-assign": "^4.1.1",
622 | "signal-exit": "^3.0.0",
623 | "string-width": "^4.2.3",
624 | "strip-ansi": "^6.0.1",
625 | "wide-align": "^1.1.2"
626 | },
627 | "engines": {
628 | "node": ">=10"
629 | }
630 | },
631 | "node_modules/get-intrinsic": {
632 | "version": "1.2.4",
633 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
634 | "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
635 | "dependencies": {
636 | "es-errors": "^1.3.0",
637 | "function-bind": "^1.1.2",
638 | "has-proto": "^1.0.1",
639 | "has-symbols": "^1.0.3",
640 | "hasown": "^2.0.0"
641 | },
642 | "engines": {
643 | "node": ">= 0.4"
644 | },
645 | "funding": {
646 | "url": "https://github.com/sponsors/ljharb"
647 | }
648 | },
649 | "node_modules/glob": {
650 | "version": "7.2.3",
651 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
652 | "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
653 | "dependencies": {
654 | "fs.realpath": "^1.0.0",
655 | "inflight": "^1.0.4",
656 | "inherits": "2",
657 | "minimatch": "^3.1.1",
658 | "once": "^1.3.0",
659 | "path-is-absolute": "^1.0.0"
660 | },
661 | "engines": {
662 | "node": "*"
663 | },
664 | "funding": {
665 | "url": "https://github.com/sponsors/isaacs"
666 | }
667 | },
668 | "node_modules/glob-parent": {
669 | "version": "5.1.2",
670 | "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
671 | "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
672 | "dependencies": {
673 | "is-glob": "^4.0.1"
674 | },
675 | "engines": {
676 | "node": ">= 6"
677 | }
678 | },
679 | "node_modules/gopd": {
680 | "version": "1.0.1",
681 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
682 | "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
683 | "dependencies": {
684 | "get-intrinsic": "^1.1.3"
685 | },
686 | "funding": {
687 | "url": "https://github.com/sponsors/ljharb"
688 | }
689 | },
690 | "node_modules/has-flag": {
691 | "version": "3.0.0",
692 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
693 | "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
694 | "engines": {
695 | "node": ">=4"
696 | }
697 | },
698 | "node_modules/has-property-descriptors": {
699 | "version": "1.0.2",
700 | "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
701 | "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
702 | "dependencies": {
703 | "es-define-property": "^1.0.0"
704 | },
705 | "funding": {
706 | "url": "https://github.com/sponsors/ljharb"
707 | }
708 | },
709 | "node_modules/has-proto": {
710 | "version": "1.0.3",
711 | "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz",
712 | "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==",
713 | "engines": {
714 | "node": ">= 0.4"
715 | },
716 | "funding": {
717 | "url": "https://github.com/sponsors/ljharb"
718 | }
719 | },
720 | "node_modules/has-symbols": {
721 | "version": "1.0.3",
722 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
723 | "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
724 | "engines": {
725 | "node": ">= 0.4"
726 | },
727 | "funding": {
728 | "url": "https://github.com/sponsors/ljharb"
729 | }
730 | },
731 | "node_modules/has-unicode": {
732 | "version": "2.0.1",
733 | "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
734 | "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
735 | },
736 | "node_modules/hasown": {
737 | "version": "2.0.2",
738 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
739 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
740 | "dependencies": {
741 | "function-bind": "^1.1.2"
742 | },
743 | "engines": {
744 | "node": ">= 0.4"
745 | }
746 | },
747 | "node_modules/http-errors": {
748 | "version": "2.0.0",
749 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
750 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
751 | "dependencies": {
752 | "depd": "2.0.0",
753 | "inherits": "2.0.4",
754 | "setprototypeof": "1.2.0",
755 | "statuses": "2.0.1",
756 | "toidentifier": "1.0.1"
757 | },
758 | "engines": {
759 | "node": ">= 0.8"
760 | }
761 | },
762 | "node_modules/https-proxy-agent": {
763 | "version": "5.0.1",
764 | "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
765 | "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
766 | "dependencies": {
767 | "agent-base": "6",
768 | "debug": "4"
769 | },
770 | "engines": {
771 | "node": ">= 6"
772 | }
773 | },
774 | "node_modules/https-proxy-agent/node_modules/debug": {
775 | "version": "4.3.4",
776 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
777 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
778 | "dependencies": {
779 | "ms": "2.1.2"
780 | },
781 | "engines": {
782 | "node": ">=6.0"
783 | },
784 | "peerDependenciesMeta": {
785 | "supports-color": {
786 | "optional": true
787 | }
788 | }
789 | },
790 | "node_modules/https-proxy-agent/node_modules/ms": {
791 | "version": "2.1.2",
792 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
793 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
794 | },
795 | "node_modules/iconv-lite": {
796 | "version": "0.4.24",
797 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
798 | "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
799 | "dependencies": {
800 | "safer-buffer": ">= 2.1.2 < 3"
801 | },
802 | "engines": {
803 | "node": ">=0.10.0"
804 | }
805 | },
806 | "node_modules/ignore-by-default": {
807 | "version": "1.0.1",
808 | "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
809 | "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA=="
810 | },
811 | "node_modules/inflight": {
812 | "version": "1.0.6",
813 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
814 | "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
815 | "dependencies": {
816 | "once": "^1.3.0",
817 | "wrappy": "1"
818 | }
819 | },
820 | "node_modules/inherits": {
821 | "version": "2.0.4",
822 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
823 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
824 | },
825 | "node_modules/ipaddr.js": {
826 | "version": "1.9.1",
827 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
828 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
829 | "engines": {
830 | "node": ">= 0.10"
831 | }
832 | },
833 | "node_modules/is-binary-path": {
834 | "version": "2.1.0",
835 | "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
836 | "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
837 | "dependencies": {
838 | "binary-extensions": "^2.0.0"
839 | },
840 | "engines": {
841 | "node": ">=8"
842 | }
843 | },
844 | "node_modules/is-extglob": {
845 | "version": "2.1.1",
846 | "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
847 | "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
848 | "engines": {
849 | "node": ">=0.10.0"
850 | }
851 | },
852 | "node_modules/is-fullwidth-code-point": {
853 | "version": "3.0.0",
854 | "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
855 | "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
856 | "engines": {
857 | "node": ">=8"
858 | }
859 | },
860 | "node_modules/is-glob": {
861 | "version": "4.0.3",
862 | "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
863 | "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
864 | "dependencies": {
865 | "is-extglob": "^2.1.1"
866 | },
867 | "engines": {
868 | "node": ">=0.10.0"
869 | }
870 | },
871 | "node_modules/is-number": {
872 | "version": "7.0.0",
873 | "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
874 | "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
875 | "engines": {
876 | "node": ">=0.12.0"
877 | }
878 | },
879 | "node_modules/jsonwebtoken": {
880 | "version": "9.0.2",
881 | "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
882 | "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
883 | "dependencies": {
884 | "jws": "^3.2.2",
885 | "lodash.includes": "^4.3.0",
886 | "lodash.isboolean": "^3.0.3",
887 | "lodash.isinteger": "^4.0.4",
888 | "lodash.isnumber": "^3.0.3",
889 | "lodash.isplainobject": "^4.0.6",
890 | "lodash.isstring": "^4.0.1",
891 | "lodash.once": "^4.0.0",
892 | "ms": "^2.1.1",
893 | "semver": "^7.5.4"
894 | },
895 | "engines": {
896 | "node": ">=12",
897 | "npm": ">=6"
898 | }
899 | },
900 | "node_modules/jsonwebtoken/node_modules/ms": {
901 | "version": "2.1.3",
902 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
903 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
904 | },
905 | "node_modules/jwa": {
906 | "version": "1.4.1",
907 | "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
908 | "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
909 | "dependencies": {
910 | "buffer-equal-constant-time": "1.0.1",
911 | "ecdsa-sig-formatter": "1.0.11",
912 | "safe-buffer": "^5.0.1"
913 | }
914 | },
915 | "node_modules/jws": {
916 | "version": "3.2.2",
917 | "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
918 | "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
919 | "dependencies": {
920 | "jwa": "^1.4.1",
921 | "safe-buffer": "^5.0.1"
922 | }
923 | },
924 | "node_modules/kareem": {
925 | "version": "2.5.1",
926 | "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
927 | "integrity": "sha512-7jFxRVm+jD+rkq3kY0iZDJfsO2/t4BBPeEb2qKn2lR/9KhuksYk5hxzfRYWMPV8P/x2d0kHD306YyWLzjjH+uA==",
928 | "engines": {
929 | "node": ">=12.0.0"
930 | }
931 | },
932 | "node_modules/lodash.includes": {
933 | "version": "4.3.0",
934 | "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
935 | "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
936 | },
937 | "node_modules/lodash.isboolean": {
938 | "version": "3.0.3",
939 | "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
940 | "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
941 | },
942 | "node_modules/lodash.isinteger": {
943 | "version": "4.0.4",
944 | "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
945 | "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
946 | },
947 | "node_modules/lodash.isnumber": {
948 | "version": "3.0.3",
949 | "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
950 | "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
951 | },
952 | "node_modules/lodash.isplainobject": {
953 | "version": "4.0.6",
954 | "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
955 | "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
956 | },
957 | "node_modules/lodash.isstring": {
958 | "version": "4.0.1",
959 | "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
960 | "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
961 | },
962 | "node_modules/lodash.once": {
963 | "version": "4.1.1",
964 | "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
965 | "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
966 | },
967 | "node_modules/lru-cache": {
968 | "version": "6.0.0",
969 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
970 | "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
971 | "dependencies": {
972 | "yallist": "^4.0.0"
973 | },
974 | "engines": {
975 | "node": ">=10"
976 | }
977 | },
978 | "node_modules/make-dir": {
979 | "version": "3.1.0",
980 | "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
981 | "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==",
982 | "dependencies": {
983 | "semver": "^6.0.0"
984 | },
985 | "engines": {
986 | "node": ">=8"
987 | },
988 | "funding": {
989 | "url": "https://github.com/sponsors/sindresorhus"
990 | }
991 | },
992 | "node_modules/make-dir/node_modules/semver": {
993 | "version": "6.3.1",
994 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
995 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
996 | "bin": {
997 | "semver": "bin/semver.js"
998 | }
999 | },
1000 | "node_modules/media-typer": {
1001 | "version": "0.3.0",
1002 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
1003 | "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
1004 | "engines": {
1005 | "node": ">= 0.6"
1006 | }
1007 | },
1008 | "node_modules/memory-pager": {
1009 | "version": "1.5.0",
1010 | "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz",
1011 | "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="
1012 | },
1013 | "node_modules/merge-descriptors": {
1014 | "version": "1.0.1",
1015 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
1016 | "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
1017 | },
1018 | "node_modules/methods": {
1019 | "version": "1.1.2",
1020 | "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
1021 | "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
1022 | "engines": {
1023 | "node": ">= 0.6"
1024 | }
1025 | },
1026 | "node_modules/mime": {
1027 | "version": "1.6.0",
1028 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1029 | "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1030 | "bin": {
1031 | "mime": "cli.js"
1032 | },
1033 | "engines": {
1034 | "node": ">=4"
1035 | }
1036 | },
1037 | "node_modules/mime-db": {
1038 | "version": "1.52.0",
1039 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
1040 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
1041 | "engines": {
1042 | "node": ">= 0.6"
1043 | }
1044 | },
1045 | "node_modules/mime-types": {
1046 | "version": "2.1.35",
1047 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
1048 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
1049 | "dependencies": {
1050 | "mime-db": "1.52.0"
1051 | },
1052 | "engines": {
1053 | "node": ">= 0.6"
1054 | }
1055 | },
1056 | "node_modules/minimatch": {
1057 | "version": "3.1.2",
1058 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
1059 | "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
1060 | "dependencies": {
1061 | "brace-expansion": "^1.1.7"
1062 | },
1063 | "engines": {
1064 | "node": "*"
1065 | }
1066 | },
1067 | "node_modules/minipass": {
1068 | "version": "5.0.0",
1069 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
1070 | "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
1071 | "engines": {
1072 | "node": ">=8"
1073 | }
1074 | },
1075 | "node_modules/minizlib": {
1076 | "version": "2.1.2",
1077 | "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
1078 | "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
1079 | "dependencies": {
1080 | "minipass": "^3.0.0",
1081 | "yallist": "^4.0.0"
1082 | },
1083 | "engines": {
1084 | "node": ">= 8"
1085 | }
1086 | },
1087 | "node_modules/minizlib/node_modules/minipass": {
1088 | "version": "3.3.6",
1089 | "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
1090 | "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
1091 | "dependencies": {
1092 | "yallist": "^4.0.0"
1093 | },
1094 | "engines": {
1095 | "node": ">=8"
1096 | }
1097 | },
1098 | "node_modules/mkdirp": {
1099 | "version": "1.0.4",
1100 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
1101 | "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
1102 | "bin": {
1103 | "mkdirp": "bin/cmd.js"
1104 | },
1105 | "engines": {
1106 | "node": ">=10"
1107 | }
1108 | },
1109 | "node_modules/mongodb": {
1110 | "version": "6.3.0",
1111 | "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz",
1112 | "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==",
1113 | "dependencies": {
1114 | "@mongodb-js/saslprep": "^1.1.0",
1115 | "bson": "^6.2.0",
1116 | "mongodb-connection-string-url": "^3.0.0"
1117 | },
1118 | "engines": {
1119 | "node": ">=16.20.1"
1120 | },
1121 | "peerDependencies": {
1122 | "@aws-sdk/credential-providers": "^3.188.0",
1123 | "@mongodb-js/zstd": "^1.1.0",
1124 | "gcp-metadata": "^5.2.0",
1125 | "kerberos": "^2.0.1",
1126 | "mongodb-client-encryption": ">=6.0.0 <7",
1127 | "snappy": "^7.2.2",
1128 | "socks": "^2.7.1"
1129 | },
1130 | "peerDependenciesMeta": {
1131 | "@aws-sdk/credential-providers": {
1132 | "optional": true
1133 | },
1134 | "@mongodb-js/zstd": {
1135 | "optional": true
1136 | },
1137 | "gcp-metadata": {
1138 | "optional": true
1139 | },
1140 | "kerberos": {
1141 | "optional": true
1142 | },
1143 | "mongodb-client-encryption": {
1144 | "optional": true
1145 | },
1146 | "snappy": {
1147 | "optional": true
1148 | },
1149 | "socks": {
1150 | "optional": true
1151 | }
1152 | }
1153 | },
1154 | "node_modules/mongodb-connection-string-url": {
1155 | "version": "3.0.0",
1156 | "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz",
1157 | "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==",
1158 | "dependencies": {
1159 | "@types/whatwg-url": "^11.0.2",
1160 | "whatwg-url": "^13.0.0"
1161 | }
1162 | },
1163 | "node_modules/mongoose": {
1164 | "version": "8.2.2",
1165 | "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.2.2.tgz",
1166 | "integrity": "sha512-6sMxe1d3k/dBjiOX4ExNTNOP0g1x0iq8eXyg+ttgIXM3HLnQ0IUyXRwVVAPFFY6O4/8uYN5dB0Ec72FrexbPpw==",
1167 | "dependencies": {
1168 | "bson": "^6.2.0",
1169 | "kareem": "2.5.1",
1170 | "mongodb": "6.3.0",
1171 | "mpath": "0.9.0",
1172 | "mquery": "5.0.0",
1173 | "ms": "2.1.3",
1174 | "sift": "16.0.1"
1175 | },
1176 | "engines": {
1177 | "node": ">=16.20.1"
1178 | },
1179 | "funding": {
1180 | "type": "opencollective",
1181 | "url": "https://opencollective.com/mongoose"
1182 | }
1183 | },
1184 | "node_modules/mongoose/node_modules/ms": {
1185 | "version": "2.1.3",
1186 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1187 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1188 | },
1189 | "node_modules/mpath": {
1190 | "version": "0.9.0",
1191 | "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz",
1192 | "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==",
1193 | "engines": {
1194 | "node": ">=4.0.0"
1195 | }
1196 | },
1197 | "node_modules/mquery": {
1198 | "version": "5.0.0",
1199 | "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz",
1200 | "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==",
1201 | "dependencies": {
1202 | "debug": "4.x"
1203 | },
1204 | "engines": {
1205 | "node": ">=14.0.0"
1206 | }
1207 | },
1208 | "node_modules/mquery/node_modules/debug": {
1209 | "version": "4.3.4",
1210 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1211 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1212 | "dependencies": {
1213 | "ms": "2.1.2"
1214 | },
1215 | "engines": {
1216 | "node": ">=6.0"
1217 | },
1218 | "peerDependenciesMeta": {
1219 | "supports-color": {
1220 | "optional": true
1221 | }
1222 | }
1223 | },
1224 | "node_modules/mquery/node_modules/ms": {
1225 | "version": "2.1.2",
1226 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1227 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1228 | },
1229 | "node_modules/ms": {
1230 | "version": "2.0.0",
1231 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
1232 | "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
1233 | },
1234 | "node_modules/negotiator": {
1235 | "version": "0.6.3",
1236 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
1237 | "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
1238 | "engines": {
1239 | "node": ">= 0.6"
1240 | }
1241 | },
1242 | "node_modules/node-addon-api": {
1243 | "version": "5.1.0",
1244 | "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
1245 | "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA=="
1246 | },
1247 | "node_modules/node-fetch": {
1248 | "version": "2.7.0",
1249 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
1250 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
1251 | "dependencies": {
1252 | "whatwg-url": "^5.0.0"
1253 | },
1254 | "engines": {
1255 | "node": "4.x || >=6.0.0"
1256 | },
1257 | "peerDependencies": {
1258 | "encoding": "^0.1.0"
1259 | },
1260 | "peerDependenciesMeta": {
1261 | "encoding": {
1262 | "optional": true
1263 | }
1264 | }
1265 | },
1266 | "node_modules/node-fetch/node_modules/tr46": {
1267 | "version": "0.0.3",
1268 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
1269 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
1270 | },
1271 | "node_modules/node-fetch/node_modules/webidl-conversions": {
1272 | "version": "3.0.1",
1273 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
1274 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
1275 | },
1276 | "node_modules/node-fetch/node_modules/whatwg-url": {
1277 | "version": "5.0.0",
1278 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
1279 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
1280 | "dependencies": {
1281 | "tr46": "~0.0.3",
1282 | "webidl-conversions": "^3.0.0"
1283 | }
1284 | },
1285 | "node_modules/nodemon": {
1286 | "version": "3.1.0",
1287 | "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
1288 | "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
1289 | "dependencies": {
1290 | "chokidar": "^3.5.2",
1291 | "debug": "^4",
1292 | "ignore-by-default": "^1.0.1",
1293 | "minimatch": "^3.1.2",
1294 | "pstree.remy": "^1.1.8",
1295 | "semver": "^7.5.3",
1296 | "simple-update-notifier": "^2.0.0",
1297 | "supports-color": "^5.5.0",
1298 | "touch": "^3.1.0",
1299 | "undefsafe": "^2.0.5"
1300 | },
1301 | "bin": {
1302 | "nodemon": "bin/nodemon.js"
1303 | },
1304 | "engines": {
1305 | "node": ">=10"
1306 | },
1307 | "funding": {
1308 | "type": "opencollective",
1309 | "url": "https://opencollective.com/nodemon"
1310 | }
1311 | },
1312 | "node_modules/nodemon/node_modules/debug": {
1313 | "version": "4.3.4",
1314 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
1315 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
1316 | "dependencies": {
1317 | "ms": "2.1.2"
1318 | },
1319 | "engines": {
1320 | "node": ">=6.0"
1321 | },
1322 | "peerDependenciesMeta": {
1323 | "supports-color": {
1324 | "optional": true
1325 | }
1326 | }
1327 | },
1328 | "node_modules/nodemon/node_modules/ms": {
1329 | "version": "2.1.2",
1330 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
1331 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
1332 | },
1333 | "node_modules/nopt": {
1334 | "version": "5.0.0",
1335 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz",
1336 | "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==",
1337 | "dependencies": {
1338 | "abbrev": "1"
1339 | },
1340 | "bin": {
1341 | "nopt": "bin/nopt.js"
1342 | },
1343 | "engines": {
1344 | "node": ">=6"
1345 | }
1346 | },
1347 | "node_modules/normalize-path": {
1348 | "version": "3.0.0",
1349 | "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
1350 | "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
1351 | "engines": {
1352 | "node": ">=0.10.0"
1353 | }
1354 | },
1355 | "node_modules/npmlog": {
1356 | "version": "5.0.1",
1357 | "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz",
1358 | "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==",
1359 | "dependencies": {
1360 | "are-we-there-yet": "^2.0.0",
1361 | "console-control-strings": "^1.1.0",
1362 | "gauge": "^3.0.0",
1363 | "set-blocking": "^2.0.0"
1364 | }
1365 | },
1366 | "node_modules/object-assign": {
1367 | "version": "4.1.1",
1368 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
1369 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
1370 | "engines": {
1371 | "node": ">=0.10.0"
1372 | }
1373 | },
1374 | "node_modules/object-inspect": {
1375 | "version": "1.13.1",
1376 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
1377 | "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
1378 | "funding": {
1379 | "url": "https://github.com/sponsors/ljharb"
1380 | }
1381 | },
1382 | "node_modules/on-finished": {
1383 | "version": "2.4.1",
1384 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
1385 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
1386 | "dependencies": {
1387 | "ee-first": "1.1.1"
1388 | },
1389 | "engines": {
1390 | "node": ">= 0.8"
1391 | }
1392 | },
1393 | "node_modules/once": {
1394 | "version": "1.4.0",
1395 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
1396 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
1397 | "dependencies": {
1398 | "wrappy": "1"
1399 | }
1400 | },
1401 | "node_modules/parseurl": {
1402 | "version": "1.3.3",
1403 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
1404 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
1405 | "engines": {
1406 | "node": ">= 0.8"
1407 | }
1408 | },
1409 | "node_modules/path-is-absolute": {
1410 | "version": "1.0.1",
1411 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
1412 | "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
1413 | "engines": {
1414 | "node": ">=0.10.0"
1415 | }
1416 | },
1417 | "node_modules/path-to-regexp": {
1418 | "version": "0.1.7",
1419 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
1420 | "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
1421 | },
1422 | "node_modules/picomatch": {
1423 | "version": "2.3.1",
1424 | "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
1425 | "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
1426 | "engines": {
1427 | "node": ">=8.6"
1428 | },
1429 | "funding": {
1430 | "url": "https://github.com/sponsors/jonschlinkert"
1431 | }
1432 | },
1433 | "node_modules/proxy-addr": {
1434 | "version": "2.0.7",
1435 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
1436 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
1437 | "dependencies": {
1438 | "forwarded": "0.2.0",
1439 | "ipaddr.js": "1.9.1"
1440 | },
1441 | "engines": {
1442 | "node": ">= 0.10"
1443 | }
1444 | },
1445 | "node_modules/pstree.remy": {
1446 | "version": "1.1.8",
1447 | "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
1448 | "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w=="
1449 | },
1450 | "node_modules/punycode": {
1451 | "version": "2.3.1",
1452 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
1453 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
1454 | "engines": {
1455 | "node": ">=6"
1456 | }
1457 | },
1458 | "node_modules/qs": {
1459 | "version": "6.11.0",
1460 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
1461 | "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
1462 | "dependencies": {
1463 | "side-channel": "^1.0.4"
1464 | },
1465 | "engines": {
1466 | "node": ">=0.6"
1467 | },
1468 | "funding": {
1469 | "url": "https://github.com/sponsors/ljharb"
1470 | }
1471 | },
1472 | "node_modules/range-parser": {
1473 | "version": "1.2.1",
1474 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
1475 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
1476 | "engines": {
1477 | "node": ">= 0.6"
1478 | }
1479 | },
1480 | "node_modules/raw-body": {
1481 | "version": "2.5.2",
1482 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
1483 | "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
1484 | "dependencies": {
1485 | "bytes": "3.1.2",
1486 | "http-errors": "2.0.0",
1487 | "iconv-lite": "0.4.24",
1488 | "unpipe": "1.0.0"
1489 | },
1490 | "engines": {
1491 | "node": ">= 0.8"
1492 | }
1493 | },
1494 | "node_modules/readable-stream": {
1495 | "version": "3.6.2",
1496 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
1497 | "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
1498 | "dependencies": {
1499 | "inherits": "^2.0.3",
1500 | "string_decoder": "^1.1.1",
1501 | "util-deprecate": "^1.0.1"
1502 | },
1503 | "engines": {
1504 | "node": ">= 6"
1505 | }
1506 | },
1507 | "node_modules/readdirp": {
1508 | "version": "3.6.0",
1509 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
1510 | "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
1511 | "dependencies": {
1512 | "picomatch": "^2.2.1"
1513 | },
1514 | "engines": {
1515 | "node": ">=8.10.0"
1516 | }
1517 | },
1518 | "node_modules/rimraf": {
1519 | "version": "3.0.2",
1520 | "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
1521 | "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
1522 | "dependencies": {
1523 | "glob": "^7.1.3"
1524 | },
1525 | "bin": {
1526 | "rimraf": "bin.js"
1527 | },
1528 | "funding": {
1529 | "url": "https://github.com/sponsors/isaacs"
1530 | }
1531 | },
1532 | "node_modules/safe-buffer": {
1533 | "version": "5.2.1",
1534 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
1535 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
1536 | "funding": [
1537 | {
1538 | "type": "github",
1539 | "url": "https://github.com/sponsors/feross"
1540 | },
1541 | {
1542 | "type": "patreon",
1543 | "url": "https://www.patreon.com/feross"
1544 | },
1545 | {
1546 | "type": "consulting",
1547 | "url": "https://feross.org/support"
1548 | }
1549 | ]
1550 | },
1551 | "node_modules/safer-buffer": {
1552 | "version": "2.1.2",
1553 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
1554 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
1555 | },
1556 | "node_modules/semver": {
1557 | "version": "7.6.0",
1558 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
1559 | "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
1560 | "dependencies": {
1561 | "lru-cache": "^6.0.0"
1562 | },
1563 | "bin": {
1564 | "semver": "bin/semver.js"
1565 | },
1566 | "engines": {
1567 | "node": ">=10"
1568 | }
1569 | },
1570 | "node_modules/send": {
1571 | "version": "0.18.0",
1572 | "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
1573 | "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
1574 | "dependencies": {
1575 | "debug": "2.6.9",
1576 | "depd": "2.0.0",
1577 | "destroy": "1.2.0",
1578 | "encodeurl": "~1.0.2",
1579 | "escape-html": "~1.0.3",
1580 | "etag": "~1.8.1",
1581 | "fresh": "0.5.2",
1582 | "http-errors": "2.0.0",
1583 | "mime": "1.6.0",
1584 | "ms": "2.1.3",
1585 | "on-finished": "2.4.1",
1586 | "range-parser": "~1.2.1",
1587 | "statuses": "2.0.1"
1588 | },
1589 | "engines": {
1590 | "node": ">= 0.8.0"
1591 | }
1592 | },
1593 | "node_modules/send/node_modules/ms": {
1594 | "version": "2.1.3",
1595 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1596 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
1597 | },
1598 | "node_modules/serve-static": {
1599 | "version": "1.15.0",
1600 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
1601 | "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
1602 | "dependencies": {
1603 | "encodeurl": "~1.0.2",
1604 | "escape-html": "~1.0.3",
1605 | "parseurl": "~1.3.3",
1606 | "send": "0.18.0"
1607 | },
1608 | "engines": {
1609 | "node": ">= 0.8.0"
1610 | }
1611 | },
1612 | "node_modules/set-blocking": {
1613 | "version": "2.0.0",
1614 | "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
1615 | "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="
1616 | },
1617 | "node_modules/set-function-length": {
1618 | "version": "1.2.2",
1619 | "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
1620 | "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
1621 | "dependencies": {
1622 | "define-data-property": "^1.1.4",
1623 | "es-errors": "^1.3.0",
1624 | "function-bind": "^1.1.2",
1625 | "get-intrinsic": "^1.2.4",
1626 | "gopd": "^1.0.1",
1627 | "has-property-descriptors": "^1.0.2"
1628 | },
1629 | "engines": {
1630 | "node": ">= 0.4"
1631 | }
1632 | },
1633 | "node_modules/setprototypeof": {
1634 | "version": "1.2.0",
1635 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
1636 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
1637 | },
1638 | "node_modules/side-channel": {
1639 | "version": "1.0.6",
1640 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz",
1641 | "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==",
1642 | "dependencies": {
1643 | "call-bind": "^1.0.7",
1644 | "es-errors": "^1.3.0",
1645 | "get-intrinsic": "^1.2.4",
1646 | "object-inspect": "^1.13.1"
1647 | },
1648 | "engines": {
1649 | "node": ">= 0.4"
1650 | },
1651 | "funding": {
1652 | "url": "https://github.com/sponsors/ljharb"
1653 | }
1654 | },
1655 | "node_modules/sift": {
1656 | "version": "16.0.1",
1657 | "resolved": "https://registry.npmjs.org/sift/-/sift-16.0.1.tgz",
1658 | "integrity": "sha512-Wv6BjQ5zbhW7VFefWusVP33T/EM0vYikCaQ2qR8yULbsilAT8/wQaXvuQ3ptGLpoKx+lihJE3y2UTgKDyyNHZQ=="
1659 | },
1660 | "node_modules/signal-exit": {
1661 | "version": "3.0.7",
1662 | "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
1663 | "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="
1664 | },
1665 | "node_modules/simple-update-notifier": {
1666 | "version": "2.0.0",
1667 | "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
1668 | "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
1669 | "dependencies": {
1670 | "semver": "^7.5.3"
1671 | },
1672 | "engines": {
1673 | "node": ">=10"
1674 | }
1675 | },
1676 | "node_modules/sparse-bitfield": {
1677 | "version": "3.0.3",
1678 | "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz",
1679 | "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==",
1680 | "dependencies": {
1681 | "memory-pager": "^1.0.2"
1682 | }
1683 | },
1684 | "node_modules/statuses": {
1685 | "version": "2.0.1",
1686 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
1687 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
1688 | "engines": {
1689 | "node": ">= 0.8"
1690 | }
1691 | },
1692 | "node_modules/string_decoder": {
1693 | "version": "1.3.0",
1694 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
1695 | "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
1696 | "dependencies": {
1697 | "safe-buffer": "~5.2.0"
1698 | }
1699 | },
1700 | "node_modules/string-width": {
1701 | "version": "4.2.3",
1702 | "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
1703 | "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
1704 | "dependencies": {
1705 | "emoji-regex": "^8.0.0",
1706 | "is-fullwidth-code-point": "^3.0.0",
1707 | "strip-ansi": "^6.0.1"
1708 | },
1709 | "engines": {
1710 | "node": ">=8"
1711 | }
1712 | },
1713 | "node_modules/strip-ansi": {
1714 | "version": "6.0.1",
1715 | "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
1716 | "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
1717 | "dependencies": {
1718 | "ansi-regex": "^5.0.1"
1719 | },
1720 | "engines": {
1721 | "node": ">=8"
1722 | }
1723 | },
1724 | "node_modules/supports-color": {
1725 | "version": "5.5.0",
1726 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
1727 | "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
1728 | "dependencies": {
1729 | "has-flag": "^3.0.0"
1730 | },
1731 | "engines": {
1732 | "node": ">=4"
1733 | }
1734 | },
1735 | "node_modules/tar": {
1736 | "version": "6.2.0",
1737 | "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz",
1738 | "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==",
1739 | "dependencies": {
1740 | "chownr": "^2.0.0",
1741 | "fs-minipass": "^2.0.0",
1742 | "minipass": "^5.0.0",
1743 | "minizlib": "^2.1.1",
1744 | "mkdirp": "^1.0.3",
1745 | "yallist": "^4.0.0"
1746 | },
1747 | "engines": {
1748 | "node": ">=10"
1749 | }
1750 | },
1751 | "node_modules/to-regex-range": {
1752 | "version": "5.0.1",
1753 | "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
1754 | "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
1755 | "dependencies": {
1756 | "is-number": "^7.0.0"
1757 | },
1758 | "engines": {
1759 | "node": ">=8.0"
1760 | }
1761 | },
1762 | "node_modules/toidentifier": {
1763 | "version": "1.0.1",
1764 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
1765 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
1766 | "engines": {
1767 | "node": ">=0.6"
1768 | }
1769 | },
1770 | "node_modules/touch": {
1771 | "version": "3.1.0",
1772 | "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
1773 | "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
1774 | "dependencies": {
1775 | "nopt": "~1.0.10"
1776 | },
1777 | "bin": {
1778 | "nodetouch": "bin/nodetouch.js"
1779 | }
1780 | },
1781 | "node_modules/touch/node_modules/nopt": {
1782 | "version": "1.0.10",
1783 | "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
1784 | "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
1785 | "dependencies": {
1786 | "abbrev": "1"
1787 | },
1788 | "bin": {
1789 | "nopt": "bin/nopt.js"
1790 | },
1791 | "engines": {
1792 | "node": "*"
1793 | }
1794 | },
1795 | "node_modules/tr46": {
1796 | "version": "4.1.1",
1797 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
1798 | "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
1799 | "dependencies": {
1800 | "punycode": "^2.3.0"
1801 | },
1802 | "engines": {
1803 | "node": ">=14"
1804 | }
1805 | },
1806 | "node_modules/type-is": {
1807 | "version": "1.6.18",
1808 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
1809 | "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
1810 | "dependencies": {
1811 | "media-typer": "0.3.0",
1812 | "mime-types": "~2.1.24"
1813 | },
1814 | "engines": {
1815 | "node": ">= 0.6"
1816 | }
1817 | },
1818 | "node_modules/undefsafe": {
1819 | "version": "2.0.5",
1820 | "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
1821 | "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA=="
1822 | },
1823 | "node_modules/unpipe": {
1824 | "version": "1.0.0",
1825 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
1826 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
1827 | "engines": {
1828 | "node": ">= 0.8"
1829 | }
1830 | },
1831 | "node_modules/util-deprecate": {
1832 | "version": "1.0.2",
1833 | "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
1834 | "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
1835 | },
1836 | "node_modules/utils-merge": {
1837 | "version": "1.0.1",
1838 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
1839 | "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
1840 | "engines": {
1841 | "node": ">= 0.4.0"
1842 | }
1843 | },
1844 | "node_modules/vary": {
1845 | "version": "1.1.2",
1846 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
1847 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
1848 | "engines": {
1849 | "node": ">= 0.8"
1850 | }
1851 | },
1852 | "node_modules/webidl-conversions": {
1853 | "version": "7.0.0",
1854 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
1855 | "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
1856 | "engines": {
1857 | "node": ">=12"
1858 | }
1859 | },
1860 | "node_modules/whatwg-url": {
1861 | "version": "13.0.0",
1862 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
1863 | "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
1864 | "dependencies": {
1865 | "tr46": "^4.1.1",
1866 | "webidl-conversions": "^7.0.0"
1867 | },
1868 | "engines": {
1869 | "node": ">=16"
1870 | }
1871 | },
1872 | "node_modules/wide-align": {
1873 | "version": "1.1.5",
1874 | "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz",
1875 | "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==",
1876 | "dependencies": {
1877 | "string-width": "^1.0.2 || 2 || 3 || 4"
1878 | }
1879 | },
1880 | "node_modules/wrappy": {
1881 | "version": "1.0.2",
1882 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
1883 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
1884 | },
1885 | "node_modules/yallist": {
1886 | "version": "4.0.0",
1887 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
1888 | "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
1889 | }
1890 | }
1891 | }
1892 |
--------------------------------------------------------------------------------