+
No items in Cart
-
+
)
diff --git a/client/src/pages/cart/components/index.tsx b/client/src/pages/cart/components/index.tsx
new file mode 100644
index 0000000..6932f36
--- /dev/null
+++ b/client/src/pages/cart/components/index.tsx
@@ -0,0 +1,6 @@
+import CartItem from './cart-items/CartItems'
+import CartPriceDetails from './cart-price-details/CartPriceDetails'
+import EmptyCart from './empty-cart/EmptyCart'
+import PlaceOrder from './place-order/PlaceOrder'
+
+export { CartItem, CartPriceDetails, EmptyCart, PlaceOrder }
diff --git a/client/src/pages/cart/components/place-order/PlaceOrder.module.css b/client/src/pages/cart/components/place-order/PlaceOrder.module.css
new file mode 100644
index 0000000..e8b1941
--- /dev/null
+++ b/client/src/pages/cart/components/place-order/PlaceOrder.module.css
@@ -0,0 +1,21 @@
+.place-order {
+ text-align: end;
+ background-color: white;
+ box-shadow: 0 -2px 10px 0 rgba(0, 0, 0, 0.1);
+}
+
+.place-order button {
+ margin: 1rem 1rem;
+ padding: 1rem;
+ border: 0;
+ outline: 0;
+ background-color: var(--color-brand-orange);
+ right: 30px;
+ bottom: 20px;
+ cursor: pointer;
+ border-radius: 3px;
+ width: 15rem;
+ color: white;
+ font-size: 1rem;
+ font-weight: 500;
+}
diff --git a/client/src/pages/cart/components/place-order/PlaceOrder.tsx b/client/src/pages/cart/components/place-order/PlaceOrder.tsx
index 0ea8d3f..8b57792 100644
--- a/client/src/pages/cart/components/place-order/PlaceOrder.tsx
+++ b/client/src/pages/cart/components/place-order/PlaceOrder.tsx
@@ -1,14 +1,25 @@
-import { totalCartPrice } from '@/utils'
import axios from 'axios'
-type Props = {
- cartData: []
+import useServerStatus from '@/hooks/useServerStatus'
+import { useAppSelector } from '@/state/store'
+import { totalCartPrice } from '@/utils'
+import toast from 'react-hot-toast'
+import styles from './PlaceOrder.module.css'
+
+declare global {
+ interface Window {
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ Razorpay: any
+ }
}
-export default function PlaceOrder({ cartData }: Props) {
+export default function PlaceOrder() {
+ const cartData = useAppSelector((state) => state.cart)
+ const serverStatus = useServerStatus()
+
const amount = totalCartPrice(cartData)
- const handleCheckout = async () => {
+ const performCheckout = async () => {
const {
data: { key },
} = await axios.get('http://www.localhost:5000/api/v1/get-key')
@@ -24,7 +35,7 @@ export default function PlaceOrder({ cartData }: Props) {
amount: order.amount,
currency: 'INR',
name: 'RazorPay',
- description: 'Tutorial of RazorPay',
+ description: 'Secure payment through RazorPay',
// image: 'https://avatars.githubusercontent.com/u/25058652?v=4',
order_id: order.id,
callback_url: 'http://localhost:5000/api/v1/paymentverification',
@@ -32,14 +43,27 @@ export default function PlaceOrder({ cartData }: Props) {
address: 'Razorpay Corporate Office',
},
}
- // @ts-expect-error Custom Razorpay script in index.html
+
const razor = new window.Razorpay(options)
razor.open()
}
+ const handleCheckout = async () => {
+ if (!serverStatus?.status) {
+ // @ts-expect-error ...
+ return toast.error(serverStatus?.message)
+ }
+ if (serverStatus?.status)
+ toast.promise(performCheckout(), {
+ loading: 'Processing...',
+ success: 'Done!',
+ error: 'Server is not running',
+ })
+ }
+
return (
-
+
)
diff --git a/client/src/pages/home/HomePage.module.css b/client/src/pages/home/HomePage.module.css
index 107e2e8..2700b41 100644
--- a/client/src/pages/home/HomePage.module.css
+++ b/client/src/pages/home/HomePage.module.css
@@ -1,3 +1,10 @@
+.home-container {
+ height: 100vh;
+ max-width: 100vw;
+ outline: 1px solid red;
+ margin: 1rem;
+}
+
.home-btn {
border: 0;
padding: 0.5rem 1rem;
diff --git a/client/src/pages/home/HomePage.tsx b/client/src/pages/home/HomePage.tsx
index caffcd4..848fd62 100644
--- a/client/src/pages/home/HomePage.tsx
+++ b/client/src/pages/home/HomePage.tsx
@@ -1,18 +1,12 @@
-import classNames from 'classnames/bind'
import { Link } from 'react-router-dom'
-
-import Button from '@/components/ui/Button'
import styles from './HomePage.module.css'
-const cx = classNames.bind(styles)
-
export default function HomePage() {
return (
- <>
+
HomePage
Product Listing
Cart
-
- >
+
)
}
diff --git a/client/src/pages/index.ts b/client/src/pages/index.ts
deleted file mode 100644
index 0b9a69b..0000000
--- a/client/src/pages/index.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-import CartPage from './cart/CartPage'
-import HomePage from './home/HomePage'
-import LoginPage from './login/LoginPage'
-import ProductDetailsPage from './product-details/ProductDetailsPage'
-import ProductListingPage from './product-listing/ProductListingPage'
-import SignUpPage from './signup/SignUpPage'
-
-export {
- CartPage,
- HomePage,
- LoginPage,
- ProductDetailsPage,
- ProductListingPage,
- SignUpPage,
-}
diff --git a/client/src/pages/login/LoginPage.module.css b/client/src/pages/login/LoginPage.module.css
deleted file mode 100644
index 5993646..0000000
--- a/client/src/pages/login/LoginPage.module.css
+++ /dev/null
@@ -1,75 +0,0 @@
-.login-container {
- margin: 1.4rem auto;
- width: 50rem;
- height: 31rem;
- background-color: white;
- display: flex;
- box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.08);
-}
-
-.left {
- width: 24rem;
- background-color: #2874f0;
- padding: 2.2rem;
- color: white;
- height: 100%;
-}
-
-.login-title {
- font-size: 1.7rem;
- font-weight: 500;
- margin: 0.5rem 0;
-}
-
-.login-desc {
- color: #dbdbdb;
- font-size: 1.1rem;
-}
-
-.right {
- padding: 2rem;
- width: 35rem;
- height: 100%;
-}
-
-.right form {
- margin-top: 2rem;
- display: flex;
- align-items: center;
- flex-direction: column;
- gap: 0.8rem;
-}
-
-form input {
- width: 100%;
- padding: 0.4rem 1rem;
- margin: 0.6rem 0;
- outline: 0;
- font-size: 1.1rem;
- border: 0;
- border-bottom: 1px solid grey;
-}
-input::placeholder {
- opacity: 0.7;
-}
-
-form button {
- border: 0;
- background-color: #fb641b;
- box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.2);
- width: 100%;
- height: 3rem;
- cursor: pointer;
- font-size: 1.1rem;
- color: white;
-}
-
-small {
- text-align: left;
- font-size: 0.7rem;
- color: #878787;
-}
-
-.demo-link {
- color: #2874f0;
-}
diff --git a/client/src/pages/login/LoginPage.tsx b/client/src/pages/login/LoginPage.tsx
index 3efc2db..4f68958 100644
--- a/client/src/pages/login/LoginPage.tsx
+++ b/client/src/pages/login/LoginPage.tsx
@@ -1,40 +1,14 @@
-import classNames from 'classnames/bind'
-import { Link } from 'react-router-dom'
-import styles from './LoginPage.module.css'
-
-const cx = classNames.bind(styles)
+import Form from '@/components/form/Form'
export default function LoginPage() {
return (
-
-
-
- Login
-
-
-
Get access to your Orders,
-
Wishlist and Recommendations
-
-
-
-
+ <>
+
+ >
)
}
diff --git a/client/src/pages/payment-success/PaymentSuccess.tsx b/client/src/pages/payment-success/PaymentSuccessPage.tsx
similarity index 87%
rename from client/src/pages/payment-success/PaymentSuccess.tsx
rename to client/src/pages/payment-success/PaymentSuccessPage.tsx
index 53cb867..174ba4b 100644
--- a/client/src/pages/payment-success/PaymentSuccess.tsx
+++ b/client/src/pages/payment-success/PaymentSuccessPage.tsx
@@ -3,7 +3,7 @@ import { useAppDispatch } from '@/state/store'
import { useEffect } from 'react'
import { useSearchParams } from 'react-router-dom'
-export default function PaymentSuccess() {
+export default function PaymentSuccessPage() {
const seachQuery = useSearchParams()[0]
const referenceNum = seachQuery.get('reference')
@@ -14,7 +14,7 @@ export default function PaymentSuccess() {
if (referenceNum) {
console.log(referenceNum)
const response = await fetch(
- `http://localhost:5000/verify-payment?paymentId=${referenceNum}`
+ `http://localhost:5000/api/v1/verify-payment?paymentId=${referenceNum}`
)
const data = await response.json()
if (data.success) {
diff --git a/client/src/pages/product-details/ProductDetailsPage.module.css b/client/src/pages/product-details/ProductDetailsPage.module.css
new file mode 100644
index 0000000..de7c63c
--- /dev/null
+++ b/client/src/pages/product-details/ProductDetailsPage.module.css
@@ -0,0 +1,40 @@
+.product-details-container {
+ display: flex;
+ min-height: 100dvh;
+ max-width: 80rem;
+ margin: 0 auto;
+ gap: 2rem;
+ margin-top: 1rem;
+}
+
+.product-image {
+ height: 20rem;
+ width: 20rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+
+.product-image img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: cover;
+}
+
+.go-to-cart-page,
+.add-to-cart-btn {
+ margin: 1rem 1rem;
+ padding: 1rem;
+ border: 0;
+ outline: 0;
+ background-color: var(--color-brand-orange);
+ right: 30px;
+ bottom: 20px;
+ cursor: pointer;
+ border-radius: 3px;
+ width: 15rem;
+ color: white;
+ font-size: 1rem;
+ font-weight: 500;
+}
diff --git a/client/src/pages/product-details/ProductDetailsPage.tsx b/client/src/pages/product-details/ProductDetailsPage.tsx
index 7b1236e..436ab70 100644
--- a/client/src/pages/product-details/ProductDetailsPage.tsx
+++ b/client/src/pages/product-details/ProductDetailsPage.tsx
@@ -1,40 +1,51 @@
import { Link, useParams } from 'react-router-dom'
-import { useState } from 'react'
+import Loader from '@/components/loader/Loader'
import useHandleDispatch from '@/hooks/useHandleDispatch'
import { useGetProductByIdQuery } from '@/state/services/productApi'
+import { useAppSelector } from '@/state/store'
+import { isItemInCart } from '@/utils'
+import { Rating } from '@mui/material'
+import styles from './ProductDetailsPage.module.css'
export default function ProductDetailsPage() {
- const [isAddedToCart, setIsAddedToCart] = useState(false)
const { id } = useParams()
const { handleAddToCart } = useHandleDispatch()
+ const { data, isFetching } = useGetProductByIdQuery(String(id))
+
+ const cartData = useAppSelector((state) => state.cart)
+ if (isFetching || !data) return
- const { data } = useGetProductByIdQuery(String(id))
- console.log(data)
return (
-
+
-

+
+

+
+ {isItemInCart(cartData, data) ? (
+
+
+
+ ) : (
+
+ )}
{data?.title}
- {isAddedToCart ? (
- <>
-
- >
- ) : (
- <>
-
- >
- )}
+
{data.description}
+
{data.stock}
+
+
{data.category}
+
{data.brand}
+
${data.price}
+
{data.discountPercentage}%
)
diff --git a/client/src/pages/product-listing/ProductListingPage.tsx b/client/src/pages/product-listing/ProductListingPage.tsx
index ea6798a..4dbf5e6 100644
--- a/client/src/pages/product-listing/ProductListingPage.tsx
+++ b/client/src/pages/product-listing/ProductListingPage.tsx
@@ -1,51 +1,26 @@
-import { Link } from 'react-router-dom'
+import { memo } from 'react'
-import Box from '@mui/material/Box'
-import Paper from '@mui/material/Paper'
+import Loader from '@/components/loader/Loader'
import useFilter from '@/hooks/useFilters'
import { SidebarFilters } from '@/layouts'
-import { ProductType } from '@/types'
+import Products from './components/products/Products'
-export default function ProductListingPage() {
- const { filteredData } = useFilter()
+function ProductListingPage() {
+ const { isLoading } = useFilter()
+
+ if (isLoading) return
return (
-
-
+
+
+
-
- {filteredData?.map((product: ProductType) => (
-
-
-
-

-
{product.title}
-
{product.rating}
- {/*
{product.description}
*/}
-
-
-
-
${product.price}
-
-
-
- ))}
-
)
}
+
+const MemoizedProductListingPage = memo(ProductListingPage)
+export default MemoizedProductListingPage
diff --git a/client/src/pages/product-listing/components/product-card/ProductCard.module.css b/client/src/pages/product-listing/components/product-card/ProductCard.module.css
new file mode 100644
index 0000000..0c8b1aa
--- /dev/null
+++ b/client/src/pages/product-listing/components/product-card/ProductCard.module.css
@@ -0,0 +1,44 @@
+.product-card-wrapper {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.left {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.product-image {
+ height: 12rem;
+ width: 12rem;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+
+.product-image img {
+ max-width: 100%;
+ max-height: 100%;
+ object-fit: cover;
+}
+
+.right {
+ margin-right: 5rem;
+}
+
+.right h2 {
+ font-weight: 500;
+ font-size: 25px;
+}
+
+.product-desc {
+ max-width: 30rem;
+}
+
+.product-discount {
+ color: #388e3c;
+ font-size: 13px;
+}
diff --git a/client/src/pages/product-listing/components/product-card/ProductCard.tsx b/client/src/pages/product-listing/components/product-card/ProductCard.tsx
new file mode 100644
index 0000000..c4526cd
--- /dev/null
+++ b/client/src/pages/product-listing/components/product-card/ProductCard.tsx
@@ -0,0 +1,49 @@
+import Paper from '@mui/material/Paper'
+import Rating from '@mui/material/Rating'
+import { Link } from 'react-router-dom'
+
+import type { ProductType } from '@/types'
+import styles from './ProductCard.module.css'
+
+type ProductProps = {
+ product: ProductType
+}
+
+export default function ProductCard({ product }: ProductProps) {
+ return (
+
+
+
+
+
+

+
+
+
{product.title}
+
+
{product.description}
+
+
+
+
${product.price}
+
+ {product.discountPercentage}% off
+
+
+
+
+
+ )
+}
diff --git a/client/src/pages/product-listing/components/products/Products.tsx b/client/src/pages/product-listing/components/products/Products.tsx
new file mode 100644
index 0000000..141ea76
--- /dev/null
+++ b/client/src/pages/product-listing/components/products/Products.tsx
@@ -0,0 +1,15 @@
+import useFilter from '@/hooks/useFilters'
+import type { ProductType } from '@/types'
+import ProductCard from '../product-card/ProductCard'
+
+export default function Products() {
+ const { filteredData } = useFilter()
+
+ return (
+ <>
+ {filteredData?.map((product: ProductType) => (
+
+ ))}
+ >
+ )
+}
diff --git a/client/src/pages/signup/SignUpPage.tsx b/client/src/pages/signup/SignUpPage.tsx
index 4c6d340..c181d06 100644
--- a/client/src/pages/signup/SignUpPage.tsx
+++ b/client/src/pages/signup/SignUpPage.tsx
@@ -1,40 +1,14 @@
-import classNames from 'classnames/bind'
-import { Link } from 'react-router-dom'
-import styles from './SignUpPage.module.css'
-
-const cx = classNames.bind(styles)
+import Form from '@/components/form/Form'
export default function SignUpPage() {
return (
-
-
-
- Login
-
-
-
Get access to your Orders,
-
Wishlist and Recommendations
-
-
-
-
+ <>
+
+ >
)
}
diff --git a/client/src/routes.tsx b/client/src/routes.tsx
index 04a2ae0..0ed667c 100644
--- a/client/src/routes.tsx
+++ b/client/src/routes.tsx
@@ -7,9 +7,8 @@ import {
import App from './App'
import {
CartPage,
- HomePage,
LoginPage,
- PaymentSuccess,
+ PaymentSuccessPage,
ProductDetailsPage,
ProductListingPage,
SignUpPage,
@@ -18,13 +17,13 @@ import {
const router = createBrowserRouter(
createRoutesFromElements(
}>
-
} />
-
} />
+ {/*
} /> */}
+
} />
} />
} />
} />
} />
-
} />
+
} />
)
)
diff --git a/client/src/state/services/productApi.ts b/client/src/state/services/productApi.ts
index 7363fe3..6d48680 100644
--- a/client/src/state/services/productApi.ts
+++ b/client/src/state/services/productApi.ts
@@ -1,13 +1,13 @@
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
import { ENV } from '@/conf'
-import { ProductType } from '@/types'
+import type { ProductType } from '@/types'
export const productApi = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({ baseUrl: ENV.API_BASE_URL }),
tagTypes: ['product'],
endpoints: (builder) => ({
- getAllProducts: builder.query
({
+ getAllProducts: builder.query({
query: () => '/products',
providesTags: ['product'],
}),
diff --git a/client/src/state/slices/cartSlice.ts b/client/src/state/slices/cartSlice.ts
index 2cecdcf..7e4d705 100644
--- a/client/src/state/slices/cartSlice.ts
+++ b/client/src/state/slices/cartSlice.ts
@@ -1,5 +1,5 @@
-import { CartType } from '@/types'
-import { PayloadAction, createSlice, nanoid } from '@reduxjs/toolkit'
+import type { CartType } from '@/types'
+import { createSlice, nanoid } from '@reduxjs/toolkit'
const initialState = JSON.parse(localStorage.getItem('cart') || '[]')
@@ -7,7 +7,7 @@ const cartSlice = createSlice({
name: 'cart',
initialState,
reducers: {
- addToCart(state, action: PayloadAction) {
+ addToCart(state, action) {
const item = state.find((item: CartType) => item.id === action.payload.id)
if (item) {
item.quantity += 1
@@ -29,7 +29,7 @@ const cartSlice = createSlice({
}
localStorage.setItem('cart', JSON.stringify(state))
},
- removeFromCart(state, action: PayloadAction) {
+ removeFromCart(state, action) {
const newState = state.filter(
(item: CartType) => item.cartItemId !== action.payload
)
@@ -38,7 +38,7 @@ const cartSlice = createSlice({
},
clear() {
return []
- }
+ },
},
})
diff --git a/client/src/state/slices/filtersSlice.ts b/client/src/state/slices/filtersSlice.ts
deleted file mode 100644
index c292d86..0000000
--- a/client/src/state/slices/filtersSlice.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import { createSlice, PayloadAction } from '@reduxjs/toolkit'
-import { ProductType } from '@/types'
-
-type InitialStateType = {
- data: ProductType[]
- sort: string
- stateRating: string | null
- priceRange: number[]
- searchQuery: string
-}
-
-export const initialState: InitialStateType = {
- data: [],
- sort: '',
- stateRating: null,
- priceRange: [10, 2000],
- searchQuery: '',
-}
-
-const filtersSlice = createSlice({
- name: 'filters',
- initialState,
- reducers: {
- sort(state, action: PayloadAction) {
- state.sort = action.payload
- },
- filterRating(state, action: PayloadAction) {
- state.stateRating = action.payload
- },
- priceRange(state, action) {
- state.priceRange = action.payload
- },
- filterSearch(state, action) {
- state.searchQuery = action.payload
- },
- clearFilters() {
- return initialState
- },
- },
-})
-
-export const { filterRating, sort, priceRange, filterSearch, clearFilters } =
- filtersSlice.actions
-
-export default filtersSlice.reducer
diff --git a/client/src/state/slices/index.ts b/client/src/state/slices/index.ts
index eba2234..af9b954 100644
--- a/client/src/state/slices/index.ts
+++ b/client/src/state/slices/index.ts
@@ -1,4 +1,3 @@
import cartReducer from './cartSlice'
-import filtersReducer from './filtersSlice'
-export { cartReducer, filtersReducer }
+export { cartReducer }
diff --git a/client/src/state/store.ts b/client/src/state/store.ts
index 768e9a1..ec4841a 100644
--- a/client/src/state/store.ts
+++ b/client/src/state/store.ts
@@ -1,19 +1,22 @@
import { configureStore } from '@reduxjs/toolkit'
-import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
+import type { TypedUseSelectorHook } from 'react-redux'
+import { useDispatch, useSelector } from 'react-redux'
+import { setupListeners } from '@reduxjs/toolkit/query'
import { productApi } from './services'
-import { cartReducer, filtersReducer } from './slices'
+import { cartReducer } from './slices'
const store = configureStore({
reducer: {
cart: cartReducer,
- filter: filtersReducer,
[productApi.reducerPath]: productApi.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(productApi.middleware),
})
+setupListeners(store.dispatch)
+
// Types for store.
type RootState = ReturnType
type AppDispatch = typeof store.dispatch
diff --git a/client/src/types/index.ts b/client/src/types/index.ts
index bc7e7e2..c34cfed 100644
--- a/client/src/types/index.ts
+++ b/client/src/types/index.ts
@@ -9,19 +9,10 @@ export type ProductType = {
brand: string
category: string
thumbnail: string
+ images: string[]
}
-export type CartType = {
- id: number
- title: string
- description: string
- price: number
- discountPercentage: number
- rating: number
- stock: number
- brand: string
- category: string
- thumbnail: string
+export type CartType = ProductType & {
quantity: number
cartItemId: string
}
diff --git a/client/src/utils/index.ts b/client/src/utils/index.ts
index c39ec4a..7adb851 100644
--- a/client/src/utils/index.ts
+++ b/client/src/utils/index.ts
@@ -1,4 +1,4 @@
-import { CartType } from '../types'
+import type { CartType, ProductType } from '@/types'
export const totalCartPrice = (cartData: CartType[]) => {
let cartPrice = 0
@@ -7,3 +7,39 @@ export const totalCartPrice = (cartData: CartType[]) => {
}
return cartPrice
}
+
+type ClassesType = string | boolean | null | undefined
+
+export const cx = (...classes: ClassesType[]) => {
+ return classes.filter(Boolean).join(' ')
+}
+
+export const isItemInCart = (cartData: CartType[], data: ProductType) => {
+ return cartData?.map((item: CartType) => item.id).includes(data?.id)
+}
+
+export const getOriginalAndThumbnailImg = (images: string[]) => {
+ return images?.map((url) => {
+ return {
+ original: url,
+ thumbnail: url,
+ }
+ })
+}
+
+export const calculateTotalDiscount = (cartData: CartType[]): number => {
+ let totalDiscount = 0
+ for (let i = 0; i < cartData.length; i++) {
+ const discountAmount = calculateSavings(
+ cartData[i].price,
+ cartData[i].discountPercentage
+ )
+ totalDiscount += discountAmount * (cartData[i].quantity || 1)
+ }
+ return Math.round(totalDiscount)
+}
+
+function calculateSavings(price: number, discountPercentage: number): number {
+ const discountAmount = (price * discountPercentage) / 100
+ return discountAmount
+}
diff --git a/client/src/vite-env.d.ts b/client/src/vite-env.d.ts
index a712a4f..6381127 100644
--- a/client/src/vite-env.d.ts
+++ b/client/src/vite-env.d.ts
@@ -3,5 +3,6 @@
interface ImportMeta {
env: {
VITE_API_BASE_URL: string
+ VITE_SERVER_URL: string
}
}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 8b4a93d..fc2d12f 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -22,6 +22,7 @@
}
},
"include": ["src"],
+ "exclude": ["node_modules"],
"references": [
{
"path": "./tsconfig.node.json"
diff --git a/client/vite.config.ts b/client/vite.config.ts
index b89b1f9..5651885 100644
--- a/client/vite.config.ts
+++ b/client/vite.config.ts
@@ -5,16 +5,39 @@ import react from '@vitejs/plugin-react'
import path from 'path'
import { defineConfig } from 'vite'
-export default defineConfig({
- plugins: [react()],
- resolve: {
- alias: {
- '@': path.resolve(__dirname, './src'),
+type ViteConfigInput = {
+ mode: string
+ command: string
+}
+
+export default (args: ViteConfigInput) => {
+ const generateScopedName =
+ args.mode === 'development'
+ ? '[name]_[local]_[hash:base64:4]'
+ : '[hash:base64:6]'
+
+ return defineConfig({
+ plugins: [react()],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ test: {
+ globals: true,
+ environment: 'jsdom',
+ setupFiles: '__test__/setup',
+ },
+ css: {
+ modules: {
+ localsConvention: 'camelCase',
+ generateScopedName,
+ },
+ },
+ server: {
+ proxy: {
+ '/api': 'http://localhost:5000',
+ },
},
- },
- test: {
- globals: true,
- environment: 'jsdom',
- setupFiles: '__test__/setup',
- },
-})
+ })
+}
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..246e27a
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,721 @@
+{
+ "name": "e-commerce",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "devDependencies": {
+ "husky": "^9.0.10",
+ "lint-staged": "^15.2.2"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.0.tgz",
+ "integrity": "sha512-kzRaCqXnpzWs+3z5ABPQiVke+iq0KXkHo8xiWV4RPTi5Yli0l97BEQuhXV1s7+aSU/fu1kUuxgS4MsQ0fRuygw==",
+ "dev": true,
+ "dependencies": {
+ "type-fest": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+ "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+ "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "dev": true,
+ "dependencies": {
+ "fill-range": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
+ "integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
+ "dev": true,
+ "dependencies": {
+ "restore-cursor": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true
+ },
+ "node_modules/commander": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
+ "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.3.4",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+ "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "dev": true,
+ "dependencies": {
+ "ms": "2.1.2"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.3.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz",
+ "integrity": "sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==",
+ "dev": true
+ },
+ "node_modules/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+ "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "dev": true,
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.2.0.tgz",
+ "integrity": "sha512-2nk+7SIVb14QrgXFHcm84tD4bKQz0RxPuMT8Ag5KPOq7J5fEmAg0UbXdTOSHqNuHSU28k55qnceesxXRZGzKWA==",
+ "dev": true,
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.0.10",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.0.10.tgz",
+ "integrity": "sha512-TQGNknoiy6bURzIO77pPRu+XHi6zI7T93rX+QnJsoYFf3xdjKOur+IlfqzJGMHIK/wXrLg+GsvMs8Op7vI2jVA==",
+ "dev": true,
+ "bin": {
+ "husky": "bin.mjs"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true
+ },
+ "node_modules/lilconfig": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz",
+ "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/lint-staged": {
+ "version": "15.2.2",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.2.tgz",
+ "integrity": "sha512-TiTt93OPh1OZOsb5B7k96A/ATl2AjIZo+vnzFZ6oHK5FuTk63ByDtxGQpHm+kFETjEWqgkF95M8FRXKR/LEBcw==",
+ "dev": true,
+ "dependencies": {
+ "chalk": "5.3.0",
+ "commander": "11.1.0",
+ "debug": "4.3.4",
+ "execa": "8.0.1",
+ "lilconfig": "3.0.0",
+ "listr2": "8.0.1",
+ "micromatch": "4.0.5",
+ "pidtree": "0.6.0",
+ "string-argv": "0.3.2",
+ "yaml": "2.3.4"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.0.1.tgz",
+ "integrity": "sha512-ovJXBXkKGfq+CwmKTjluEqFi3p4h8xvkxGQQAQan22YCgef4KZ1mKGjzfGh6PL6AW5Csw0QiQPNuQyH+6Xk3hA==",
+ "dev": true,
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.0.0",
+ "rfdc": "^1.3.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/log-update": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.0.0.tgz",
+ "integrity": "sha512-niTvB4gqvtof056rRIrTZvjNYE4rCUzO6X/X+kYjd7WFxXeJ0NwEFnRxX6ehkvv3jTwrXnNdtAak5XYZuIyPFw==",
+ "dev": true,
+ "dependencies": {
+ "ansi-escapes": "^6.2.0",
+ "cli-cursor": "^4.0.0",
+ "slice-ansi": "^7.0.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+ "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+ "dev": true,
+ "dependencies": {
+ "braces": "^3.0.2",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+ "dev": true
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.2.0.tgz",
+ "integrity": "sha512-W4/tgAXFqFA0iL7fk0+uQ3g7wkL8xJmx3XdK0VGb4cHW//eZTtKGvFBBoRKVTpY7n6ze4NL9ly7rgXcHufqXKg==",
+ "dev": true,
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
+ "integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
+ "dev": true,
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true
+ },
+ "node_modules/rfdc": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.1.tgz",
+ "integrity": "sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg==",
+ "dev": true
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.1.0.tgz",
+ "integrity": "sha512-SEIJCWiX7Kg4c129n48aDRwLbFb2LJmXXFrWBG4NGaRtMQ3myKPKbwrD1BKqQn74oCoNMBVrfDEr5M9YxCsrkw==",
+ "dev": true,
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "3.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz",
+ "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==",
+ "dev": true,
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.3.4",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz",
+ "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==",
+ "dev": true,
+ "engines": {
+ "node": ">= 14"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..32e4ae2
--- /dev/null
+++ b/package.json
@@ -0,0 +1,26 @@
+{
+ "devDependencies": {
+ "husky": "^9.0.10",
+ "lint-staged": "^15.2.2"
+ },
+ "scripts": {
+ "prepare": "husky",
+ "check-format-client": "cd client && npm run check-format",
+ "check-format-server": "cd server && npm run check-format",
+ "check-lint": "cd client && npm run check-lint",
+ "check-types": "cd client && npm run check-types"
+ },
+ "lint-staged": {
+ "client/src/**/*.{ts,tsx}": [
+ "npm run check-format-client",
+ "npm run check-types",
+ "npm run check-lint"
+ ],
+ "client/src/**/*.{ts,tsx,css,scss}": [
+ "npm run check-format-client"
+ ],
+ "server/src/**/*.{js,jsx,css,scss}": [
+ "npm run check-format-server"
+ ]
+ }
+}
diff --git a/server/.env.sample b/server/.env.sample
new file mode 100644
index 0000000..39478e1
--- /dev/null
+++ b/server/.env.sample
@@ -0,0 +1,7 @@
+MONGODB_URL=
+MONGODB_URL_LOCAL=mongodb://127.0.0.1:27017
+
+RAZORPAY_API_KEY=
+RAZORPAY_APT_SECRET=
+
+CORS_ORIGIN=*
\ No newline at end of file
diff --git a/server/package-lock.json b/server/package-lock.json
index 66878a4..1b9be7d 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -8,22 +8,23 @@
"name": "server",
"version": "1.0.0",
"dependencies": {
+ "bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
- "mongoose": "^8.0.3",
- "pg": "^8.11.3",
+ "jsonwebtoken": "^9.0.2",
+ "mongoose": "^8.1.1",
"razorpay": "^2.9.2"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/node": "^20.10.5",
- "@types/pg": "^8.10.9",
- "nodemon": "^3.0.2",
+ "nodemon": "^3.0.3",
"prettier": "^3.1.1",
- "ts-node": "^10.9.2"
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
}
},
"node_modules/@cspotcode/source-map-support": {
@@ -64,9 +65,9 @@
}
},
"node_modules/@mongodb-js/saslprep": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.1.tgz",
- "integrity": "sha512-t7c5K033joZZMspnHg/gWPE4kandgc2OxE74aYOtGKfgB9VPuVJPix0H6fhmm2erj5PBJ21mqcx34lpIGtUCsQ==",
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz",
+ "integrity": "sha512-8zJ8N1x51xo9hwPh6AWnKdLGEC5N3lDa6kms1YHmFBoRhTpJR6HG8wWk0td1MVCu9cD4YBrvjZEtd5Obw0Fbnw==",
"dependencies": {
"sparse-bitfield": "^3.0.3"
}
@@ -177,74 +178,6 @@
"undici-types": "~5.26.4"
}
},
- "node_modules/@types/pg": {
- "version": "8.10.9",
- "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.10.9.tgz",
- "integrity": "sha512-UksbANNE/f8w0wOMxVKKIrLCbEMV+oM1uKejmwXr39olg4xqcfBDbXxObJAt6XxHbDa4XTKOlUEcEltXDX+XLQ==",
- "dev": true,
- "dependencies": {
- "@types/node": "*",
- "pg-protocol": "*",
- "pg-types": "^4.0.1"
- }
- },
- "node_modules/@types/pg/node_modules/pg-types": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.1.tgz",
- "integrity": "sha512-hRCSDuLII9/LE3smys1hRHcu5QGcLs9ggT7I/TCs0IE+2Eesxi9+9RWAAwZ0yaGjxoWICF/YHLOEjydGujoJ+g==",
- "dev": true,
- "dependencies": {
- "pg-int8": "1.0.1",
- "pg-numeric": "1.0.2",
- "postgres-array": "~3.0.1",
- "postgres-bytea": "~3.0.0",
- "postgres-date": "~2.0.1",
- "postgres-interval": "^3.0.0",
- "postgres-range": "^1.1.1"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@types/pg/node_modules/postgres-array": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz",
- "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@types/pg/node_modules/postgres-bytea": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz",
- "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==",
- "dev": true,
- "dependencies": {
- "obuf": "~1.1.2"
- },
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/@types/pg/node_modules/postgres-date": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.0.1.tgz",
- "integrity": "sha512-YtMKdsDt5Ojv1wQRvUhnyDJNSr2dGIC96mQVKz7xufp07nfuFONzdaowrMHjlAzY6GDLd4f+LUHHAAM1h4MdUw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@types/pg/node_modules/postgres-interval": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz",
- "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==",
- "dev": true,
- "engines": {
- "node": ">=12"
- }
- },
"node_modules/@types/qs": {
"version": "6.9.10",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.10.tgz",
@@ -309,11 +242,10 @@
"integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="
},
"node_modules/@types/whatwg-url": {
- "version": "8.2.2",
- "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-8.2.2.tgz",
- "integrity": "sha512-FtQu10RWgn3D9U4aazdwIE2yzphmTJREDqNdODHrbrZmmMqI0vMheC/6NE/J1Yveaj8H+ela+YwWTjq5PGmuhA==",
+ "version": "11.0.4",
+ "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz",
+ "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==",
"dependencies": {
- "@types/node": "*",
"@types/webidl-conversions": "*"
}
},
@@ -448,6 +380,11 @@
"tweetnacl": "^0.14.3"
}
},
+ "node_modules/bcryptjs": {
+ "version": "2.4.3",
+ "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
+ "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
+ },
"node_modules/binary-extensions": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
@@ -508,20 +445,17 @@
}
},
"node_modules/bson": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/bson/-/bson-6.2.0.tgz",
- "integrity": "sha512-ID1cI+7bazPDyL9wYy9GaQ8gEEohWvcUl/Yf0dIdutJxnmInEEyCsb4awy/OiBfall7zBA179Pahi3vCdFze3Q==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/bson/-/bson-6.3.0.tgz",
+ "integrity": "sha512-balJfqwwTBddxfnidJZagCBPP/f48zj9Sdp3OJswREOgsJzHiQSaOIAtApSgDQFYgHqAvFkp53AFSqjMDZoTFw==",
"engines": {
"node": ">=16.20.1"
}
},
- "node_modules/buffer-writer": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
- "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
- "engines": {
- "node": ">=4"
- }
+ "node_modules/buffer-equal-constant-time": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
+ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="
},
"node_modules/bytes": {
"version": "3.1.2",
@@ -754,6 +688,14 @@
"safer-buffer": "^2.1.0"
}
},
+ "node_modules/ecdsa-sig-formatter": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
+ "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
+ "dependencies": {
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/ee-first": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
@@ -1182,6 +1124,32 @@
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
"integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="
},
+ "node_modules/jsonwebtoken": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
+ "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==",
+ "dependencies": {
+ "jws": "^3.2.2",
+ "lodash.includes": "^4.3.0",
+ "lodash.isboolean": "^3.0.3",
+ "lodash.isinteger": "^4.0.4",
+ "lodash.isnumber": "^3.0.3",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.isstring": "^4.0.1",
+ "lodash.once": "^4.0.0",
+ "ms": "^2.1.1",
+ "semver": "^7.5.4"
+ },
+ "engines": {
+ "node": ">=12",
+ "npm": ">=6"
+ }
+ },
+ "node_modules/jsonwebtoken/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
+ },
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
@@ -1196,6 +1164,25 @@
"node": ">=0.6.0"
}
},
+ "node_modules/jwa": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz",
+ "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==",
+ "dependencies": {
+ "buffer-equal-constant-time": "1.0.1",
+ "ecdsa-sig-formatter": "1.0.11",
+ "safe-buffer": "^5.0.1"
+ }
+ },
+ "node_modules/jws": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
+ "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==",
+ "dependencies": {
+ "jwa": "^1.4.1",
+ "safe-buffer": "^5.0.1"
+ }
+ },
"node_modules/kareem": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-2.5.1.tgz",
@@ -1209,11 +1196,45 @@
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
+ "node_modules/lodash.includes": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
+ "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="
+ },
+ "node_modules/lodash.isboolean": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
+ "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="
+ },
+ "node_modules/lodash.isinteger": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
+ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="
+ },
+ "node_modules/lodash.isnumber": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
+ "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="
+ },
+ "node_modules/lodash.isstring": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
+ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="
+ },
+ "node_modules/lodash.once": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
+ "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="
+ },
"node_modules/lru-cache": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
- "dev": true,
"dependencies": {
"yallist": "^4.0.0"
},
@@ -1296,13 +1317,13 @@
}
},
"node_modules/mongodb": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.2.0.tgz",
- "integrity": "sha512-d7OSuGjGWDZ5usZPqfvb36laQ9CPhnWkAGHT61x5P95p/8nMVeH8asloMwW6GcYFeB0Vj4CB/1wOTDG2RA9BFA==",
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.3.0.tgz",
+ "integrity": "sha512-tt0KuGjGtLUhLoU263+xvQmPHEGTw5LbcNC73EoFRYgSHwZt5tsoJC110hDyO1kjQzpgNrpdcSza9PknWN4LrA==",
"dependencies": {
"@mongodb-js/saslprep": "^1.1.0",
"bson": "^6.2.0",
- "mongodb-connection-string-url": "^2.6.0"
+ "mongodb-connection-string-url": "^3.0.0"
},
"engines": {
"node": ">=16.20.1"
@@ -1341,22 +1362,22 @@
}
},
"node_modules/mongodb-connection-string-url": {
- "version": "2.6.0",
- "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-2.6.0.tgz",
- "integrity": "sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==",
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.0.tgz",
+ "integrity": "sha512-t1Vf+m1I5hC2M5RJx/7AtxgABy1cZmIPQRMXw+gEIPn/cZNF3Oiy+l0UIypUwVB5trcWHq3crg2g3uAR9aAwsQ==",
"dependencies": {
- "@types/whatwg-url": "^8.2.1",
- "whatwg-url": "^11.0.0"
+ "@types/whatwg-url": "^11.0.2",
+ "whatwg-url": "^13.0.0"
}
},
"node_modules/mongoose": {
- "version": "8.0.3",
- "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.0.3.tgz",
- "integrity": "sha512-LJRT0yP4TW14HT4r2RkxqyvoTylMSzWpl5QOeVHTnRggCLQSpkoBdgbUtORFq/mSL2o9cLCPJz+6uzFj25qbHw==",
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.1.1.tgz",
+ "integrity": "sha512-DbLb0NsiEXmaqLOpEz+AtAsgwhRw6f25gwa1dF5R7jj6lS1D8X6uTdhBSC8GDVtOwe5Tfw2EL7nTn6hiJT3Bgg==",
"dependencies": {
"bson": "^6.2.0",
"kareem": "2.5.1",
- "mongodb": "6.2.0",
+ "mongodb": "6.3.0",
"mpath": "0.9.0",
"mquery": "5.0.0",
"ms": "2.1.3",
@@ -1429,9 +1450,9 @@
}
},
"node_modules/nodemon": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.2.tgz",
- "integrity": "sha512-9qIN2LNTrEzpOPBaWHTm4Asy1LxXLSickZStAQ4IZe7zsoIpD/A7LWxhZV3t4Zu352uBcqVnRsDXSMR2Sc3lTA==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz",
+ "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==",
"dev": true,
"dependencies": {
"chokidar": "^3.5.2",
@@ -1527,12 +1548,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/obuf": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz",
- "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
- "dev": true
- },
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -1544,11 +1559,6 @@
"node": ">= 0.8"
}
},
- "node_modules/packet-reader": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
- "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
- },
"node_modules/parseurl": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
@@ -1567,98 +1577,6 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow=="
},
- "node_modules/pg": {
- "version": "8.11.3",
- "resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
- "integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
- "dependencies": {
- "buffer-writer": "2.0.0",
- "packet-reader": "1.0.0",
- "pg-connection-string": "^2.6.2",
- "pg-pool": "^3.6.1",
- "pg-protocol": "^1.6.0",
- "pg-types": "^2.1.0",
- "pgpass": "1.x"
- },
- "engines": {
- "node": ">= 8.0.0"
- },
- "optionalDependencies": {
- "pg-cloudflare": "^1.1.1"
- },
- "peerDependencies": {
- "pg-native": ">=3.0.1"
- },
- "peerDependenciesMeta": {
- "pg-native": {
- "optional": true
- }
- }
- },
- "node_modules/pg-cloudflare": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
- "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
- "optional": true
- },
- "node_modules/pg-connection-string": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
- "integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
- },
- "node_modules/pg-int8": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
- "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
- "engines": {
- "node": ">=4.0.0"
- }
- },
- "node_modules/pg-numeric": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz",
- "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==",
- "dev": true,
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pg-pool": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
- "integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
- "peerDependencies": {
- "pg": ">=8.0"
- }
- },
- "node_modules/pg-protocol": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
- "integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
- },
- "node_modules/pg-types": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
- "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
- "dependencies": {
- "pg-int8": "1.0.1",
- "postgres-array": "~2.0.0",
- "postgres-bytea": "~1.0.0",
- "postgres-date": "~1.0.4",
- "postgres-interval": "^1.1.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pgpass": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
- "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
- "dependencies": {
- "split2": "^4.1.0"
- }
- },
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
@@ -1671,47 +1589,6 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/postgres-array": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
- "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/postgres-bytea": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
- "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postgres-date": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
- "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postgres-interval": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
- "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
- "dependencies": {
- "xtend": "^4.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/postgres-range": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.3.tgz",
- "integrity": "sha512-VdlZoocy5lCP0c/t66xAfclglEapXPCIVhqqJRncYpvbCgImF0w67aPKfbqUMr72tO2k5q0TdTZwCLjPTI6C9g==",
- "dev": true
- },
"node_modules/prettier": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.1.tgz",
@@ -1950,7 +1827,6 @@
"version": "7.5.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
- "dev": true,
"dependencies": {
"lru-cache": "^6.0.0"
},
@@ -2060,14 +1936,6 @@
"memory-pager": "^1.0.2"
}
},
- "node_modules/split2": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
- "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
- "engines": {
- "node": ">= 10.x"
- }
- },
"node_modules/sshpk": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
@@ -2165,14 +2033,14 @@
}
},
"node_modules/tr46": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
- "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
+ "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
"dependencies": {
- "punycode": "^2.1.1"
+ "punycode": "^2.3.0"
},
"engines": {
- "node": ">=12"
+ "node": ">=14"
}
},
"node_modules/ts-node": {
@@ -2251,7 +2119,6 @@
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
"dev": true,
- "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -2340,30 +2207,21 @@
}
},
"node_modules/whatwg-url": {
- "version": "11.0.0",
- "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
- "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==",
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz",
+ "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==",
"dependencies": {
- "tr46": "^3.0.0",
+ "tr46": "^4.1.1",
"webidl-conversions": "^7.0.0"
},
"engines": {
- "node": ">=12"
- }
- },
- "node_modules/xtend": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
- "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
- "engines": {
- "node": ">=0.4"
+ "node": ">=16"
}
},
"node_modules/yallist": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
- "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
- "dev": true
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
},
"node_modules/yn": {
"version": "3.1.1",
diff --git a/server/package.json b/server/package.json
index a6c3f17..66f0d0f 100644
--- a/server/package.json
+++ b/server/package.json
@@ -2,27 +2,30 @@
"name": "server",
"version": "1.0.0",
"main": "index.js",
- "type": "module",
"scripts": {
- "dev": "nodemon src/index.js",
- "start": "node index.js"
+ "dev": "nodemon src/index.ts",
+ "start": "node index.js",
+ "format": "prettier --write ./src",
+ "check-format": "prettier --check ./src",
+ "check-types": "tsc --pretty --noEmit"
},
"dependencies": {
+ "bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
- "mongoose": "^8.0.3",
- "pg": "^8.11.3",
+ "jsonwebtoken": "^9.0.2",
+ "mongoose": "^8.1.1",
"razorpay": "^2.9.2"
},
"devDependencies": {
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/node": "^20.10.5",
- "@types/pg": "^8.10.9",
- "nodemon": "^3.0.2",
+ "nodemon": "^3.0.3",
"prettier": "^3.1.1",
- "ts-node": "^10.9.2"
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
}
}
diff --git a/server/src/app.js b/server/src/app.js
deleted file mode 100644
index 1246e86..0000000
--- a/server/src/app.js
+++ /dev/null
@@ -1,47 +0,0 @@
-import cookieParser from 'cookie-parser'
-import cors from 'cors'
-import express from 'express'
-import { ENV } from './conf/conf.js'
-
-const app = express()
-
-app.use(
- cors({
- origin: ENV.CORS_ORIGIN,
- credentials: true,
- })
-)
-app.use(express.json({ limit: '16kb' }))
-app.use(express.urlencoded({ extended: true, limit: '16kb' }))
-app.use(express.static('public'))
-app.use(cookieParser())
-
-// Routes
-import { Payment } from './Models/payment.model.js'
-import paymentRoute from './routes/payment.routes.js'
-
-app.use('/api/v1', paymentRoute)
-
-app.get('/api/v1/get-key', (req, res) =>
- res.status(200).json({ key: ENV.RAZORPAY_API_KEY })
-)
-
-app.get('/verify-payment', async (req, res) => {
- const paymentId = req.query.paymentId
- console.log(paymentId)
- try {
- const payment = await Payment.findOne({ razorpay_payment_id: paymentId })
-
- if (payment) {
- // console.log(payment)
- res.json({ success: true })
- } else {
- // console.log(payment)
- res.json({ success: false })
- }
- } catch (error) {
- res.status(500).json({ success: false })
- }
-})
-
-export default app
diff --git a/server/src/app.ts b/server/src/app.ts
new file mode 100644
index 0000000..8c95ff1
--- /dev/null
+++ b/server/src/app.ts
@@ -0,0 +1,28 @@
+import cookieParser from 'cookie-parser'
+import cors from 'cors'
+import express from 'express'
+import { ENV } from './conf/conf'
+
+const app = express()
+
+app.use(
+ cors({
+ origin: ENV.CORS_ORIGIN,
+ credentials: true,
+ })
+)
+app.use(express.json({ limit: '16kb' }))
+app.use(express.urlencoded({ extended: true, limit: '16kb' }))
+app.use(express.static('public'))
+app.use(cookieParser())
+
+// Routes
+import generalRoute from './routes/general.route'
+import paymentRoute from './routes/payment.route'
+import userRoute from './routes/user.route'
+
+app.use('/api/v1', generalRoute)
+app.use('/api/v1', paymentRoute)
+app.use('/api/v1', userRoute)
+
+export default app
diff --git a/server/src/conf/conf.js b/server/src/conf/conf.ts
similarity index 100%
rename from server/src/conf/conf.js
rename to server/src/conf/conf.ts
diff --git a/server/src/constants.js b/server/src/constants.ts
similarity index 100%
rename from server/src/constants.js
rename to server/src/constants.ts
diff --git a/server/src/controller/general/serverStatus.controller.ts b/server/src/controller/general/serverStatus.controller.ts
new file mode 100644
index 0000000..30a568e
--- /dev/null
+++ b/server/src/controller/general/serverStatus.controller.ts
@@ -0,0 +1,3 @@
+export const general = (req, res) => {
+ res.json({ status: true, message: 'Server is running' })
+}
diff --git a/server/src/controller/payment/checkout.controller.ts b/server/src/controller/payment/checkout.controller.ts
new file mode 100644
index 0000000..d6aeda9
--- /dev/null
+++ b/server/src/controller/payment/checkout.controller.ts
@@ -0,0 +1,14 @@
+import { razorpayInstance } from '../..'
+
+export const checkout = async (req, res) => {
+ const options = {
+ amount: Number(req.body.amount * 100),
+ currency: 'INR',
+ }
+ const order = await razorpayInstance.orders.create(options)
+
+ res.status(200).json({
+ success: true,
+ order,
+ })
+}
diff --git a/server/src/controllers/payment.controller.js b/server/src/controller/payment/payment.controller.ts
similarity index 62%
rename from server/src/controllers/payment.controller.js
rename to server/src/controller/payment/payment.controller.ts
index 5019ab3..6d0b38e 100644
--- a/server/src/controllers/payment.controller.js
+++ b/server/src/controller/payment/payment.controller.ts
@@ -1,21 +1,7 @@
import crypto from 'crypto'
-import { Payment } from '../Models/payment.model.js'
-import { ENV } from '../conf/conf.js'
-import { CLIENT_BASE_URL } from '../constants.js'
-import { razorpayInstance } from '../index.js'
-
-export const checkout = async (req, res) => {
- const options = {
- amount: Number(req.body.amount * 100),
- currency: 'INR',
- }
- const order = await razorpayInstance.orders.create(options)
-
- res.status(200).json({
- success: true,
- order,
- })
-}
+import { ENV } from '../../conf/conf'
+import { CLIENT_BASE_URL } from '../../constants'
+import { Payment } from '../../model/payment.model'
export const paymentVerification = async (req, res) => {
const { razorpay_order_id, razorpay_payment_id, razorpay_signature } =
@@ -46,3 +32,7 @@ export const paymentVerification = async (req, res) => {
})
}
}
+
+export const getKey = (req, res) => {
+ res.status(200).json({ key: ENV.RAZORPAY_API_KEY })
+}
diff --git a/server/src/controller/payment/verifyPayment.controller.ts b/server/src/controller/payment/verifyPayment.controller.ts
new file mode 100644
index 0000000..e9b26de
--- /dev/null
+++ b/server/src/controller/payment/verifyPayment.controller.ts
@@ -0,0 +1,17 @@
+import { Payment } from '../../model/payment.model'
+
+export const verifyPayment = async (req, res) => {
+ const paymentId = req.query.paymentId
+ console.log(paymentId)
+ try {
+ const payment = await Payment.findOne({ razorpay_payment_id: paymentId })
+
+ if (payment) {
+ res.json({ success: true })
+ } else {
+ res.json({ success: false })
+ }
+ } catch (error) {
+ res.status(500).json({ success: false })
+ }
+}
diff --git a/server/src/controller/user/login.controller.ts b/server/src/controller/user/login.controller.ts
new file mode 100644
index 0000000..0ba6660
--- /dev/null
+++ b/server/src/controller/user/login.controller.ts
@@ -0,0 +1,39 @@
+import bcryptjs from 'bcryptjs'
+import jwt from 'jsonwebtoken'
+import { User } from '../../model/user.model'
+
+export const loginUser = async (req, res) => {
+ try {
+ const { username, password } = req.body
+
+ const user = await User.findOne({ username })
+ if (!user) {
+ return res.status(400).json({ error: 'User does not exist' })
+ }
+ // Check password
+ const validPassword = await bcryptjs.compare(password, user.password)
+ if (!validPassword) {
+ return res.status(400).json({ error: 'Invalid password' })
+ }
+
+ // Create token data for JWT
+ const tokenData = {
+ id: user._id,
+ username: user.username,
+ }
+
+ // Create token with JWT
+ const token = await jwt.sign(tokenData, 'secret', {
+ expiresIn: '1d',
+ })
+
+ res.cookie('token', token)
+ return res.json({
+ message: 'Login successful',
+ success: true,
+ token,
+ })
+ } catch (error) {
+ return res.status(500).json({ error: error.message })
+ }
+}
diff --git a/server/src/controller/user/logout.controller.ts b/server/src/controller/user/logout.controller.ts
new file mode 100644
index 0000000..298c55e
--- /dev/null
+++ b/server/src/controller/user/logout.controller.ts
@@ -0,0 +1,11 @@
+export const logout = async (req, res) => {
+ try {
+ res.cookie('token', '', { maxAge: 0 })
+ return res.json({
+ message: 'Logout successfully',
+ success: true,
+ })
+ } catch (error) {
+ return res.status(500).json({ error: error.message })
+ }
+}
diff --git a/server/src/controller/user/register.controller.ts b/server/src/controller/user/register.controller.ts
new file mode 100644
index 0000000..a34b11e
--- /dev/null
+++ b/server/src/controller/user/register.controller.ts
@@ -0,0 +1,42 @@
+import bcryptjs from 'bcryptjs'
+import jwt from 'jsonwebtoken'
+import { User } from '../../model/user.model'
+
+export const registerUser = async (req, res) => {
+ try {
+ const { username, password } = req.body
+ // console.log(name, pass)
+
+ const user = await User.findOne({ username })
+ if (user) {
+ return res.status(400).json({ data: 'User already exists' })
+ }
+ // Hash the password
+ const salt = await bcryptjs.genSalt(10)
+ const hashedPassword = await bcryptjs.hash(password, salt)
+
+ const newUser = new User({
+ username,
+ password: hashedPassword,
+ })
+ const savedUser = await newUser.save()
+
+ const tokenData = {
+ id: savedUser._id,
+ username: savedUser.username,
+ }
+
+ // Create token with JWT
+ const token = await jwt.sign(tokenData, 'secret', {
+ expiresIn: '1d',
+ })
+
+ res.cookie('token', token)
+ return res.json({ message: 'User registered successfully', token })
+ } catch (error) {
+ console.log(error)
+ return res
+ .status(500)
+ .json({ error: 'An error occurred while registering the user' })
+ }
+}
diff --git a/server/src/db/db.js b/server/src/db/db.js
deleted file mode 100644
index af5c88d..0000000
--- a/server/src/db/db.js
+++ /dev/null
@@ -1,19 +0,0 @@
-import mongoose from 'mongoose'
-import { ENV } from '../conf/conf.js'
-import { DB_NAME } from '../constants.js'
-
-const connectDB = async () => {
- try {
- const connectionInstance = await mongoose.connect(
- `${ENV.MONGODB_URL_LOCAL}/${DB_NAME}`
- )
- console.log(
- `\n MongoDB connected | DB Host :: ${connectionInstance.connection.host}`
- )
- } catch (error) {
- console.log('MongoDB connection FAILED ', error)
- process.exit(1)
- }
-}
-
-export default connectDB
diff --git a/server/src/db/db.ts b/server/src/db/db.ts
new file mode 100644
index 0000000..7f14f91
--- /dev/null
+++ b/server/src/db/db.ts
@@ -0,0 +1,14 @@
+import mongoose from 'mongoose'
+import { ENV } from '../conf/conf'
+import { DB_NAME } from '../constants'
+
+const connectDB = async () => {
+ try {
+ await mongoose.connect(`${ENV.MONGODB_URL_LOCAL}/${DB_NAME}`)
+ console.log('MongoDB connected')
+ } catch (error) {
+ console.log('MongoDB connection FAILED ', error)
+ }
+}
+
+export default connectDB
diff --git a/server/src/index.js b/server/src/index.ts
similarity index 65%
rename from server/src/index.js
rename to server/src/index.ts
index 9f397f1..d63a15c 100644
--- a/server/src/index.js
+++ b/server/src/index.ts
@@ -1,6 +1,6 @@
-import app from './app.js'
-import connectDB from './db/db.js'
import Razorpay from 'razorpay'
+import app from './app'
+import connectDB from './db/db'
const PORT = process.env.PORT || 5000
@@ -11,8 +11,6 @@ export const razorpayInstance = new Razorpay({
connectDB()
.then(() => {
- app.listen(PORT, () =>
- console.log(`\n ⚙️ Server is running at port : ${PORT}🎉`)
- )
+ app.listen(PORT, () => console.log(`Server is running at port : ${PORT}`))
})
.catch((err) => console.log('MONGO db connection failed ! ', err))
diff --git a/server/src/middleware/verifyToken.middleware.ts b/server/src/middleware/verifyToken.middleware.ts
new file mode 100644
index 0000000..ccda5cb
--- /dev/null
+++ b/server/src/middleware/verifyToken.middleware.ts
@@ -0,0 +1,17 @@
+import jwt from 'jsonwebtoken'
+
+export const verifyToken = (req, res, next) => {
+ const token = req.cookies.token
+
+ if (!token) {
+ return res.status(403).json({ error: 'Access Denied' })
+ }
+
+ try {
+ const verified = jwt.verify(token, 'secret')
+ req.user = verified
+ next()
+ } catch (err) {
+ res.status(400).json({ error: 'Token is not valid' })
+ }
+}
diff --git a/server/src/middlewares/.gitkeep b/server/src/middlewares/.gitkeep
deleted file mode 100644
index e69de29..0000000
diff --git a/server/src/Models/payment.model.js b/server/src/model/payment.model.ts
similarity index 100%
rename from server/src/Models/payment.model.js
rename to server/src/model/payment.model.ts
diff --git a/server/src/model/user.model.ts b/server/src/model/user.model.ts
new file mode 100644
index 0000000..64e1c1e
--- /dev/null
+++ b/server/src/model/user.model.ts
@@ -0,0 +1,17 @@
+import mongoose from 'mongoose'
+
+const userSchema = new mongoose.Schema(
+ {
+ username: {
+ type: String,
+ required: true,
+ },
+ password: {
+ type: String,
+ require: true,
+ },
+ },
+ { timestamps: true }
+)
+
+export const User = mongoose.model('User', userSchema)
diff --git a/server/src/routes/general.route.ts b/server/src/routes/general.route.ts
new file mode 100644
index 0000000..fed1a7e
--- /dev/null
+++ b/server/src/routes/general.route.ts
@@ -0,0 +1,8 @@
+import { Router } from 'express'
+import { general } from '../controller/general/serverStatus.controller'
+
+const generalRoute = Router()
+
+generalRoute.get('/status', general)
+
+export default generalRoute
diff --git a/server/src/routes/payment.route.ts b/server/src/routes/payment.route.ts
new file mode 100644
index 0000000..0fc3306
--- /dev/null
+++ b/server/src/routes/payment.route.ts
@@ -0,0 +1,16 @@
+import { Router } from 'express'
+import { checkout } from '../controller/payment/checkout.controller'
+import {
+ getKey,
+ paymentVerification,
+} from '../controller/payment/payment.controller'
+import { verifyPayment } from '../controller/payment/verifyPayment.controller'
+
+const paymentRoute = Router()
+
+paymentRoute.route('/checkout').post(checkout)
+paymentRoute.route('/paymentverification').post(paymentVerification)
+paymentRoute.route('/verify-payment').get(verifyPayment)
+paymentRoute.route('/get-key').get(getKey)
+
+export default paymentRoute
diff --git a/server/src/routes/payment.routes.js b/server/src/routes/payment.routes.js
deleted file mode 100644
index 9f2b081..0000000
--- a/server/src/routes/payment.routes.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Router } from 'express'
-import {
- checkout,
- paymentVerification,
-} from '../controllers/payment.controller.js'
-
-const router = Router()
-
-router.route('/checkout').post(checkout)
-router.route('/paymentverification').post(paymentVerification)
-
-export default router
diff --git a/server/src/routes/user.route.ts b/server/src/routes/user.route.ts
new file mode 100644
index 0000000..a8be25d
--- /dev/null
+++ b/server/src/routes/user.route.ts
@@ -0,0 +1,12 @@
+import { Router } from 'express'
+import { loginUser } from '../controller/user/login.controller'
+import { logout } from '../controller/user/logout.controller'
+import { registerUser } from '../controller/user/register.controller'
+
+const userRoute = Router()
+
+userRoute.route('/register').post(registerUser)
+userRoute.route('/login').post(loginUser)
+userRoute.route('/logout').get(logout)
+
+export default userRoute
diff --git a/server/src/utils/ApiError.js b/server/src/utils/ApiError.js
deleted file mode 100644
index 17ec11c..0000000
--- a/server/src/utils/ApiError.js
+++ /dev/null
@@ -1,23 +0,0 @@
-class ApiError extends Error {
- constructor(
- statusCode,
- message = 'Something went wrong',
- errors = [],
- stack = ''
- ) {
- super(message)
- this.statusCode = statusCode
- this.data = null
- this.message = message
- this.success = false
- this.errors = errors
-
- if (stack) {
- this.stack = stack
- } else {
- Error.captureStackTrace(this, this.constructor)
- }
- }
-}
-
-export default ApiError
diff --git a/server/src/utils/ApiError.ts b/server/src/utils/ApiError.ts
new file mode 100644
index 0000000..282f863
--- /dev/null
+++ b/server/src/utils/ApiError.ts
@@ -0,0 +1,22 @@
+class ApiError extends Error {
+ statusCode: number
+ data: any
+ success: boolean
+ errors: any[]
+
+ constructor(
+ statusCode: number,
+ message: string = 'Something went wrong',
+ errors: any[] = [],
+ data: any = null
+ ) {
+ super(message)
+ this.statusCode = statusCode
+ this.data = data
+ this.success = false
+ this.errors = errors
+ Error.captureStackTrace(this, this.constructor)
+ }
+}
+
+export default ApiError
diff --git a/server/src/utils/ApiResponse.js b/server/src/utils/ApiResponse.js
deleted file mode 100644
index 2a9a3f5..0000000
--- a/server/src/utils/ApiResponse.js
+++ /dev/null
@@ -1,10 +0,0 @@
-class ApiResponse {
- constructor(statusCode, data, message = 'Success') {
- this.statusCode = statusCode
- this.data = data
- this.message = message
- this.success = statusCode < 400
- }
-}
-
-export default ApiResponse
diff --git a/server/src/utils/ApiResponse.ts b/server/src/utils/ApiResponse.ts
new file mode 100644
index 0000000..1a97521
--- /dev/null
+++ b/server/src/utils/ApiResponse.ts
@@ -0,0 +1,15 @@
+class ApiResponse {
+ statusCode: any
+ data: any
+ message: string
+ success: boolean
+
+ constructor(statusCode: number, data: any, message: string = 'Success') {
+ this.statusCode = statusCode
+ this.data = data
+ this.message = message
+ this.success = this.statusCode >= 200 && this.statusCode < 300
+ }
+}
+
+export default ApiResponse
diff --git a/server/src/utils/asyncHandler.js b/server/src/utils/asyncHandler.js
deleted file mode 100644
index 6b827ac..0000000
--- a/server/src/utils/asyncHandler.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const asyncHandler = (requestHandler) => {
- return (req, res, next) => {
- Promise.resolve(requestHandler(req, res, next)).catch((err) => next(err))
- }
-}
-
-export default asyncHandler
diff --git a/server/tsconfig.json b/server/tsconfig.json
new file mode 100644
index 0000000..0ef1a39
--- /dev/null
+++ b/server/tsconfig.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "CommonJS",
+ "moduleResolution": "Node",
+ "rootDir": "src",
+ "outDir": "build",
+ "lib": ["ESNext"],
+ "strict": false,
+ "esModuleInterop": true,
+ "allowJs": true,
+ "checkJs": false,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "forceConsistentCasingInFileNames": true,
+ "allowSyntheticDefaultImports": true,
+ "isolatedModules": true
+ },
+ "include": ["src/**/*"],
+ "exclude": ["node_modules"]
+}