├── ecommerce.sql ├── Client ├── app │ ├── favicon.ico │ ├── page.tsx │ ├── cart-confirmation │ │ └── [status] │ │ │ └── page.tsx │ ├── order-confirmation │ │ └── [orderID] │ │ │ └── page.tsx │ ├── blog │ │ └── page.tsx │ ├── orders │ │ └── page.tsx │ ├── about │ │ └── page.tsx │ ├── contact │ │ └── page.tsx │ ├── policy │ │ ├── page.tsx │ │ ├── privacypolicy │ │ │ └── page.tsx │ │ ├── terms&conditions │ │ │ └── page.tsx │ │ └── refund&cancellation │ │ │ └── page.tsx │ ├── account-settings │ │ └── page.tsx │ ├── search │ │ └── [productName] │ │ │ └── page.tsx │ ├── securepayment │ │ └── page.tsx │ ├── all-reviews │ │ └── [productID] │ │ │ └── page.tsx │ ├── our-services │ │ └── page.tsx │ ├── order-detail │ │ └── [orderid] │ │ │ └── page.tsx │ ├── product │ │ └── [productID] │ │ │ └── page.tsx │ ├── categories │ │ └── [category] │ │ │ └── page.tsx │ ├── hooks.ts │ ├── sub-category │ │ └── [maincategory] │ │ │ └── [subcategory] │ │ │ └── page.tsx │ ├── api │ │ ├── signout.ts │ │ ├── dateConvert.ts │ │ ├── articleData.ts │ │ ├── sendOTP.ts │ │ ├── product.ts │ │ ├── mainCategory.ts │ │ ├── resetPass.ts │ │ ├── contact.ts │ │ ├── sessionauth.ts │ │ ├── googleAuth.ts │ │ ├── signup.ts │ │ ├── signin.ts │ │ ├── filter.ts │ │ ├── search.ts │ │ ├── subCategory.ts │ │ ├── orders.ts │ │ ├── homeData.ts │ │ ├── reviews.ts │ │ ├── itemLists.ts │ │ ├── userParams.ts │ │ └── userUpdate.ts │ ├── forgot-password │ │ └── page.tsx │ ├── store.ts │ ├── cart-checkout │ │ └── page.tsx │ ├── checkout │ │ └── [productID] │ │ │ └── [sizeID] │ │ │ └── [colorID] │ │ │ └── page.tsx │ ├── sign-in │ │ └── page.tsx │ ├── sign-up │ │ └── page.tsx │ ├── signed-out │ │ └── page.tsx │ ├── layout.tsx │ ├── App.tsx │ └── globals.css ├── public │ ├── about-1.jpg │ ├── about-2.jpg │ ├── about-3.jpg │ ├── about-5.jpg │ ├── about.jpg │ ├── support.jpg │ ├── deliveryguy.png │ ├── deliveryboxes.png │ ├── securepayment.jpg │ ├── securepayment-1.jpg │ ├── securepayment-2.jpg │ ├── securepayment-3.jpg │ ├── securepayment-4.jpg │ ├── securepayment-5.jpg │ ├── vercel.svg │ └── next.svg ├── .dockerignore ├── postcss.config.mjs ├── components │ ├── ProductUi │ │ ├── Product │ │ │ ├── NoReviews.tsx │ │ │ └── ProductNotFound.tsx │ │ └── Stars.tsx │ ├── Loading.tsx │ ├── Session.tsx │ ├── Status.tsx │ ├── ProgressBar.tsx │ ├── PolicyPage │ │ └── Policy.tsx │ ├── Orders │ │ ├── NoOrders.tsx │ │ ├── OrderNotFound.tsx │ │ ├── NotLoggedin.tsx │ │ └── Dropdown.tsx │ ├── Search │ │ └── NoProduct.tsx │ ├── Categories │ │ ├── NoProduct.tsx │ │ ├── CategoryBanner.tsx │ │ └── CategorySidebar.tsx │ ├── SubCategory │ │ └── NoProduct.tsx │ ├── Mobile-Interface │ │ ├── Menubar.tsx │ │ ├── Sidebar.tsx │ │ ├── SearchMSidebar.tsx │ │ ├── SubCategoryMSidebar.tsx │ │ ├── CategoryMSidebar.tsx │ │ └── SidebarType │ │ │ └── CategoryType.tsx │ ├── CommonPage │ │ └── Common.tsx │ ├── DropdownMenu │ │ ├── Product.tsx │ │ ├── Category.tsx │ │ └── Account.tsx │ ├── Trends.tsx │ ├── Login │ │ └── otpTimer.tsx │ ├── Account │ │ ├── Coupons.tsx │ │ ├── GiftCards.tsx │ │ ├── ProfileInfo.tsx │ │ ├── Wishlist.tsx │ │ └── Addresses.tsx │ ├── SecurePayment │ │ └── Secure.tsx │ ├── OurServices │ │ └── ServicePage.tsx │ ├── Status │ │ ├── TrendingSec.tsx │ │ └── TrendSection.tsx │ ├── Footer.tsx │ ├── DealTime.tsx │ ├── Reviews │ │ └── AllReview.tsx │ ├── About │ │ └── AboutUs.tsx │ ├── Details.tsx │ ├── Blog │ │ └── BlogPage.tsx │ ├── Tabs.tsx │ ├── Banner.tsx │ ├── Deal.tsx │ ├── Contact │ │ └── Contact.tsx │ └── Checkout │ │ ├── CartFailed.tsx │ │ ├── OrderNotFound.tsx │ │ └── PaymentFailed.tsx ├── next.config.mjs ├── tsconfig.json ├── package.json ├── controllers │ └── userData.ts ├── Helpers │ ├── MenuContext.tsx │ └── AccountDialog.tsx ├── tailwind.config.ts ├── features │ └── UIUpdates │ │ └── CartWishlist.ts └── Dockerfile ├── Server ├── .dockerignore ├── utils │ └── googleAPI.ts ├── data │ ├── SMTP.ts │ └── DB.ts ├── middleware │ ├── rateLimit.ts │ └── header_auth.ts ├── controller │ └── auth-controller.ts ├── routes.ts ├── validators │ ├── cartCheckoutValidation.ts │ ├── userOTPValidation.ts │ ├── supportValidation.ts │ └── authenticationValidation.ts ├── package.json ├── index.ts └── Dockerfile ├── website-demo-image ├── 1.png ├── 2.png ├── desktop.png └── mobile.png ├── .gitignore ├── LICENSE ├── SECURITY.md ├── docker-compose.yml └── CONTRIBUTING.md /ecommerce.sql: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/ecommerce.sql -------------------------------------------------------------------------------- /Client/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/app/favicon.ico -------------------------------------------------------------------------------- /Client/public/about-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/about-1.jpg -------------------------------------------------------------------------------- /Client/public/about-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/about-2.jpg -------------------------------------------------------------------------------- /Client/public/about-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/about-3.jpg -------------------------------------------------------------------------------- /Client/public/about-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/about-5.jpg -------------------------------------------------------------------------------- /Client/public/about.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/about.jpg -------------------------------------------------------------------------------- /Client/public/support.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/support.jpg -------------------------------------------------------------------------------- /Server/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | .env 7 | .git -------------------------------------------------------------------------------- /website-demo-image/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/website-demo-image/1.png -------------------------------------------------------------------------------- /website-demo-image/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/website-demo-image/2.png -------------------------------------------------------------------------------- /Client/public/deliveryguy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/deliveryguy.png -------------------------------------------------------------------------------- /website-demo-image/desktop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/website-demo-image/desktop.png -------------------------------------------------------------------------------- /website-demo-image/mobile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/website-demo-image/mobile.png -------------------------------------------------------------------------------- /Client/public/deliveryboxes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/deliveryboxes.png -------------------------------------------------------------------------------- /Client/public/securepayment.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment.jpg -------------------------------------------------------------------------------- /Client/public/securepayment-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment-1.jpg -------------------------------------------------------------------------------- /Client/public/securepayment-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment-2.jpg -------------------------------------------------------------------------------- /Client/public/securepayment-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment-3.jpg -------------------------------------------------------------------------------- /Client/public/securepayment-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment-4.jpg -------------------------------------------------------------------------------- /Client/public/securepayment-5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HarmanPreet-Singh-XYT/E-Commerce/HEAD/Client/public/securepayment-5.jpg -------------------------------------------------------------------------------- /Client/app/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import App from './App' 3 | const page = () => { 4 | return ( 5 | 6 | ) 7 | } 8 | 9 | export default page -------------------------------------------------------------------------------- /Client/.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | .dockerignore 3 | node_modules 4 | npm-debug.log 5 | README.md 6 | .next 7 | !.next/static 8 | !.next/standalone 9 | .git 10 | .env -------------------------------------------------------------------------------- /Client/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /Client/components/ProductUi/Product/NoReviews.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const NoReviews = () => { 4 | return ( 5 |
6 | 7 |
8 | ) 9 | } 10 | 11 | export default NoReviews -------------------------------------------------------------------------------- /Client/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: false, 4 | images: { 5 | remotePatterns: [], 6 | }, 7 | output:'standalone' 8 | }; 9 | 10 | export default nextConfig; 11 | -------------------------------------------------------------------------------- /Client/app/cart-confirmation/[status]/page.tsx: -------------------------------------------------------------------------------- 1 | import CartConfirmation from '@/components/Checkout/CartConfirmation' 2 | import React from 'react' 3 | 4 | const page = () => { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default page -------------------------------------------------------------------------------- /Client/app/order-confirmation/[orderID]/page.tsx: -------------------------------------------------------------------------------- 1 | import OrderConfirmation from '@/components/Checkout/OrderConfirmation' 2 | import React from 'react' 3 | 4 | const page = () => { 5 | return ( 6 | 7 | ) 8 | } 9 | 10 | export default page -------------------------------------------------------------------------------- /Client/app/blog/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import React from 'react' 4 | import BlogPage from '@/components/Blog/BlogPage' 5 | const page = () => { 6 | return ( 7 | 8 | ) 9 | } 10 | 11 | export default page -------------------------------------------------------------------------------- /Client/app/orders/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import Order from '@/components/Orders/Order' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/about/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import AboutUs from '@/components/About/AboutUs' 3 | import Common from '@/components/CommonPage/Common' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/contact/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import Contact from '@/components/Contact/Contact' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/policy/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import Policy from '@/components/PolicyPage/Policy' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/account-settings/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React from 'react' 3 | import Common from '@/components/CommonPage/Common' 4 | import Settings from '@/components/Account/Settings' 5 | const page = () => { 6 | return ( 7 | 8 | ) 9 | } 10 | 11 | export default page -------------------------------------------------------------------------------- /Client/app/search/[productName]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import Search from '@/components/Search/Search' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/securepayment/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import Secure from '@/components/SecurePayment/Secure' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Server/utils/googleAPI.ts: -------------------------------------------------------------------------------- 1 | import {google} from 'googleapis'; 2 | const GOOGLE_CLIENT_ID = process.env.GOOGLE_CLIENT_ID; 3 | const GOOGLE_CLIENT_SECRET = process.env.GOOGLE_CLIENT_SECRET; 4 | 5 | export const oauth2Client = new google.auth.OAuth2( 6 | GOOGLE_CLIENT_ID, 7 | GOOGLE_CLIENT_SECRET, 8 | 'postmessage' 9 | ); -------------------------------------------------------------------------------- /Client/app/all-reviews/[productID]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import AllReview from '@/components/Reviews/AllReview' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/our-services/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import ServicePage from '@/components/OurServices/ServicePage' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/order-detail/[orderid]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import OrderDetail from '@/components/Orders/OrderDetail' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/product/[productID]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import ProductPage from '@/components/ProductUi/ProductPage' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/categories/[category]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import React from 'react' 4 | import CategoryPage from '@/components/Categories/CategoryPage' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/policy/privacypolicy/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import PrivacyPolicy from '@/components/PolicyPage/PrivacyPolicy' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import type { RootState, AppDispatch } from './store' 3 | 4 | // Use throughout your app instead of plain `useDispatch` and `useSelector` 5 | export const useAppDispatch = useDispatch.withTypes() 6 | export const useAppSelector = useSelector.withTypes() -------------------------------------------------------------------------------- /Client/app/policy/terms&conditions/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import TermsConditions from '@/components/PolicyPage/TermsConditions' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/sub-category/[maincategory]/[subcategory]/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import React from 'react' 4 | import SubCategory from '@/components/SubCategory/SubCategory' 5 | const page = () => { 6 | return ( 7 | 8 | ) 9 | } 10 | 11 | export default page -------------------------------------------------------------------------------- /Client/app/policy/refund&cancellation/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Common from '@/components/CommonPage/Common' 3 | import RefundCancellation from '@/components/PolicyPage/RefundCancellation' 4 | import React from 'react' 5 | 6 | const page = () => { 7 | return ( 8 | 9 | ) 10 | } 11 | 12 | export default page -------------------------------------------------------------------------------- /Client/app/api/signout.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import { cookies } from 'next/headers'; 3 | export default async function signOutHandler() { 4 | const cookie = cookies().get('sessionhold'); 5 | if(cookie){ 6 | try { 7 | cookies().delete('sessionhold'); 8 | return true; 9 | } catch (error) { 10 | return false; 11 | } 12 | }else 13 | return false; 14 | 15 | }; -------------------------------------------------------------------------------- /Client/components/Loading.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const Loading = () => { 4 | return ( 5 |
6 |
7 |
8 |
9 | 10 |
11 | ) 12 | } 13 | 14 | export default Loading -------------------------------------------------------------------------------- /Client/components/ProductUi/Stars.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactStars from 'react-stars'; 3 | 4 | interface StarsProps { 5 | stars: number; 6 | size?: number; 7 | } 8 | 9 | const Stars: React.FC = ({ stars, size = 20 }) => { 10 | return ( 11 | 18 | ); 19 | } 20 | 21 | export default Stars; -------------------------------------------------------------------------------- /Server/data/SMTP.ts: -------------------------------------------------------------------------------- 1 | import nodemailer from 'nodemailer'; 2 | const SMTP_Creds = {user:process.env.SMTP_USER as string,pass:process.env.SMTP_PASS as string,host:process.env.SMTP_HOST as string}; 3 | const SMTP = nodemailer.createTransport({ 4 | host: SMTP_Creds.host, 5 | port: 587, 6 | secure: false, // Use `true` for port 465, `false` for all other ports 7 | auth: { 8 | user: SMTP_Creds.user, 9 | pass: SMTP_Creds.pass, 10 | }, 11 | }); 12 | export {SMTP}; -------------------------------------------------------------------------------- /Client/components/Session.tsx: -------------------------------------------------------------------------------- 1 | import React,{useLayoutEffect} from 'react' 2 | import userData from '@/controllers/userData'; 3 | import useAuth from '@/controllers/Authentication'; 4 | const Session = () => { 5 | const {checkSession} = useAuth(); 6 | const { grabUserData } = userData(); 7 | async function sync(){ 8 | await checkSession(); 9 | await grabUserData(); 10 | } 11 | useLayoutEffect(() => { 12 | sync(); 13 | }, []); 14 | return <> 15 | } 16 | 17 | export default Session; -------------------------------------------------------------------------------- /Client/app/forgot-password/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import DialogBoxes from '@/components/DialogBoxes' 3 | import { AppProvider } from '@/Helpers/AccountDialog' 4 | import React from 'react' 5 | import { Provider } from 'react-redux' 6 | import { store } from '@/app/store' 7 | import ForgotPass from '@/components/Login/ForgotPass' 8 | const page = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | ) 17 | } 18 | 19 | export default page -------------------------------------------------------------------------------- /Client/components/Status.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import TrendSection from './Status/TrendSection'; 3 | import SidebarS from './Status/SidebarS'; 4 | import Deal from './Deal'; 5 | import Products from './Products'; 6 | const Status = () => { 7 | 8 | return ( 9 |
10 | 11 |
12 | 13 | 14 | 15 |
16 | 17 |
18 | ) 19 | } 20 | 21 | export default Status -------------------------------------------------------------------------------- /Client/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Client/app/store.ts: -------------------------------------------------------------------------------- 1 | 2 | import CartWishlist from '@/features/UIUpdates/CartWishlist' 3 | import { configureStore } from '@reduxjs/toolkit' 4 | import UserAccount from '@/features/UIUpdates/UserAccount' 5 | // ... 6 | 7 | export const store = configureStore({ 8 | reducer: { 9 | cartWishlist:CartWishlist, 10 | userState:UserAccount, 11 | }, 12 | }) 13 | 14 | // Infer the `RootState` and `AppDispatch` types from the store itself 15 | export type RootState = ReturnType 16 | // Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState} 17 | export type AppDispatch = typeof store.dispatch -------------------------------------------------------------------------------- /Client/components/ProgressBar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ProgressBar = ({ sold, total }:{sold:number;total:number}) => { 4 | const percentage = (sold / total) * 100; 5 | 6 | return ( 7 |
8 |
9 |
10 |
11 |
12 |
13 | ); 14 | }; 15 | 16 | export default ProgressBar; -------------------------------------------------------------------------------- /Client/app/cart-checkout/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import { MenuProvider } from '@/Helpers/MenuContext' 3 | import React from 'react' 4 | import { AppProvider } from '@/Helpers/AccountDialog' 5 | import { Provider } from 'react-redux' 6 | import { store } from '@/app/store' 7 | import CartCheckout from '@/components/Checkout/CartCheckout' 8 | const page = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export default page -------------------------------------------------------------------------------- /Client/app/checkout/[productID]/[sizeID]/[colorID]/page.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | import Checkout from '@/components/Checkout/Checkout' 3 | import { MenuProvider } from '@/Helpers/MenuContext' 4 | import React from 'react' 5 | import { AppProvider } from '@/Helpers/AccountDialog' 6 | import { Provider } from 'react-redux' 7 | import { store } from '@/app/store' 8 | const page = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | ) 18 | } 19 | 20 | export default page -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | Client/node_modules 5 | Server/node_modules 6 | # Ignore the .env file in the root directory 7 | Client/.env 8 | Server/.env 9 | # Ignore other env files 10 | /.pnp 11 | .pnp.js 12 | .yarn/install-state.gz 13 | 14 | # testing 15 | /coverage 16 | 17 | # next.js 18 | /.next/ 19 | /out/ 20 | 21 | # production 22 | /build 23 | 24 | # misc 25 | .DS_Store 26 | *.pem 27 | 28 | # debug 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # local env files 34 | .env*.local 35 | 36 | # vercel 37 | .vercel 38 | 39 | # typescript 40 | *.tsbuildinfo 41 | next-env.d.ts 42 | -------------------------------------------------------------------------------- /Client/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["dom", "dom.iterable", "esnext"], 4 | "allowJs": true, 5 | "skipLibCheck": true, 6 | "strict": true, 7 | "noEmit": true, 8 | "esModuleInterop": true, 9 | "module": "esnext", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "isolatedModules": true, 13 | "jsx": "preserve", 14 | "incremental": true, 15 | "plugins": [ 16 | { 17 | "name": "next" 18 | } 19 | ], 20 | "paths": { 21 | "@/*": ["./*"] 22 | } 23 | }, 24 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 25 | "exclude": ["node_modules"] 26 | } 27 | -------------------------------------------------------------------------------- /Client/app/api/dateConvert.ts: -------------------------------------------------------------------------------- 1 | export default function formatDate(isoString:string) { 2 | // Create a Date object from the ISO string 3 | const date = new Date(isoString); 4 | 5 | // Define an array of month names 6 | const monthNames = [ 7 | "January", "February", "March", "April", "May", "June", 8 | "July", "August", "September", "October", "November", "December" 9 | ]; 10 | 11 | // Get the month name, day, and year from the date object 12 | const month = monthNames[date.getUTCMonth()]; 13 | const day = date.getUTCDate(); 14 | const year = date.getUTCFullYear(); 15 | 16 | // Format the date as "Month day, year" 17 | return `${month} ${day}, ${year}`; 18 | } -------------------------------------------------------------------------------- /Client/app/api/articleData.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | const url = process.env.BACKEND_URL; 9 | const authKey = process.env.AUTH_KEY as string; 10 | export default async function articlesDataHandler() { 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/articles`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Client/app/sign-in/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import DialogBoxes from '@/components/DialogBoxes' 3 | import SignIn from '@/components/Login/SignIn' 4 | import { AppProvider } from '@/Helpers/AccountDialog' 5 | import React from 'react' 6 | import { Provider } from 'react-redux' 7 | import { store } from '@/app/store' 8 | import { GoogleOAuthProvider } from '@react-oauth/google'; 9 | const page = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default page -------------------------------------------------------------------------------- /Client/app/sign-up/page.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import DialogBoxes from '@/components/DialogBoxes' 3 | import Signup from '@/components/Login/Signup' 4 | import { AppProvider } from '@/Helpers/AccountDialog' 5 | import React from 'react' 6 | import { store } from '@/app/store' 7 | import { Provider } from 'react-redux' 8 | import { GoogleOAuthProvider } from '@react-oauth/google'; 9 | const page = () => { 10 | return ( 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | ) 20 | } 21 | 22 | export default page -------------------------------------------------------------------------------- /Client/app/api/sendOTP.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | export default async function forgotOTPHandler(email:string) { 9 | const url = process.env.BACKEND_URL; 10 | const authKey = process.env.AUTH_KEY as string; 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.post(`${url}/api/user/send-forgot-otp`, {email}, { 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Client/app/api/product.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | export default async function productDataHandler({productID}:{productID:string}) { 9 | const url = process.env.BACKEND_URL; 10 | const authKey = process.env.AUTH_KEY as string; 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/product/${productID}`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Server/middleware/rateLimit.ts: -------------------------------------------------------------------------------- 1 | import { Response, NextFunction } from 'express'; 2 | import { RateLimiterMemory } from 'rate-limiter-flexible'; 3 | const rateLimiter = new RateLimiterMemory({ 4 | points: 20, // maximum number of requests allowed 5 | duration: 1, // time frame in seconds 6 | }); 7 | const rateLimiterMiddleware = (req:any, res:Response, next:NextFunction) => { 8 | rateLimiter.consume(req.ip) 9 | .then(() => { 10 | // request allowed, 11 | // proceed with handling the request 12 | next(); 13 | }) 14 | .catch(() => { 15 | // request limit exceeded, 16 | // respond with an appropriate error message 17 | res.status(429).send('Too Many Requests'); 18 | }); 19 | }; 20 | export default rateLimiterMiddleware; -------------------------------------------------------------------------------- /Client/app/api/mainCategory.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | const url = process.env.BACKEND_URL; 9 | const authKey = process.env.AUTH_KEY as string; 10 | export default async function categoryDataHandler(mainCategory:string | string[]) { 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/category/${mainCategory}`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Client/app/api/resetPass.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | export default async function resetPassHandler({email,password,otp}:{email:string,password:string,otp:string}) { 9 | const url = process.env.BACKEND_URL; 10 | const authKey = process.env.AUTH_KEY as string; 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.post(`${url}/api/user/reset-password`, {email,password,otp}, { 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Client/app/api/contact.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | const url = process.env.BACKEND_URL; 9 | const authKey = process.env.AUTH_KEY as string; 10 | export default async function contactHandler({name,email,phone,method,message}:{name:string,email:string,phone:string,method:string,message:string}) { 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.post(`${url}/api/contact`,{name,email,phone,method,message},{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; -------------------------------------------------------------------------------- /Server/middleware/header_auth.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response, NextFunction } from 'express'; 2 | import jwt from 'jsonwebtoken'; 3 | 4 | const JWT_SECRET = process.env.JWT_AUTH_KEY as string; 5 | 6 | interface AuthenticatedRequest extends Request { 7 | user?: any; 8 | } 9 | 10 | function authenticateToken(req: AuthenticatedRequest, res: Response, next: NextFunction) { 11 | const authHeader = req.headers['authorization']; 12 | const token = authHeader && authHeader.split(' ')[1]; 13 | 14 | if (!token) { 15 | return res.status(401).json({ error: 'Access denied, no token provided' }); 16 | } 17 | 18 | jwt.verify(token, JWT_SECRET, (err, user) => { 19 | if (err) { 20 | return res.status(403).json({ error: 'Invalid token' }); 21 | } 22 | req.user = user; 23 | next(); 24 | }); 25 | } 26 | 27 | export default authenticateToken; -------------------------------------------------------------------------------- /Server/controller/auth-controller.ts: -------------------------------------------------------------------------------- 1 | import { client } from "../data/DB"; 2 | import axios from 'axios' 3 | import {oauth2Client} from '../utils/googleAPI'; 4 | export const googleAuth = async (code:string) => { 5 | try { 6 | const googleRes = await oauth2Client.getToken(code); 7 | oauth2Client.setCredentials(googleRes.tokens); 8 | 9 | const userRes = await axios.get( 10 | `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${googleRes.tokens.access_token}` 11 | ); 12 | const query = ` 13 | SELECT userid,username,email,mobile_number,dob FROM "users" WHERE email = $1; 14 | `; 15 | 16 | const values = [userRes.data.email]; 17 | const result = await client.query(query, values); 18 | return result.rows[0]; 19 | } catch (error) { 20 | return false; 21 | } 22 | 23 | }; -------------------------------------------------------------------------------- /Server/routes.ts: -------------------------------------------------------------------------------- 1 | import express from 'express'; 2 | import userUpdate from './routes/userUpdate' 3 | import authentication from './routes/authentication'; 4 | import userOTP from './routes/userOTP'; 5 | import products from './routes/products'; 6 | import userDetails from './routes/userDetails' 7 | import siteData from './routes/siteData' 8 | import productCheckout from './routes/productCheckout' 9 | import cartCheckout from './routes/cartCheckout' 10 | import homeData from './routes/homeData' 11 | import support from './routes/support' 12 | const router = express.Router(); 13 | router.use('/', authentication); 14 | router.use('/update', userUpdate); 15 | router.use('/',userOTP); 16 | router.use('/',products); 17 | router.use('/',userDetails); 18 | router.use('/',siteData); 19 | router.use('/',productCheckout); 20 | router.use('/',cartCheckout); 21 | router.use('/',homeData); 22 | router.use('/',support); 23 | export default router; -------------------------------------------------------------------------------- /Client/app/api/sessionauth.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { cookies } from 'next/headers'; 4 | import { sign } from 'jsonwebtoken'; 5 | async function encrypt(key:string){ 6 | const encryptedKey = await sign({},key) 7 | return encryptedKey 8 | } 9 | export default async function sessionHandler() { 10 | const url = process.env.BACKEND_URL; 11 | const authKey = process.env.AUTH_KEY as string; 12 | const sendingKey = await encrypt(authKey); 13 | const cookie = cookies().get('sessionhold'); 14 | if(cookie){ 15 | try { 16 | const response = await axios.post(`${url}/api/user/session-check`, {token:cookie.value}, { 17 | headers: { authorization:`Bearer ${sendingKey}` }, 18 | }); 19 | return {status:response.status,data:response.data} 20 | } catch (error) { 21 | return {status:500,error: 'Internal Server Error' } 22 | } 23 | }else 24 | return {status:500,error: 'Cookie Not Found' }; 25 | 26 | }; -------------------------------------------------------------------------------- /Client/app/api/googleAuth.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | import { cookies } from 'next/headers'; 5 | async function encrypt(key:string){ 6 | const encryptedKey = await sign({},key) 7 | return encryptedKey 8 | } 9 | const url = process.env.BACKEND_URL; 10 | const authKey = process.env.AUTH_KEY as string; 11 | export default async function authDataHandler(code:string) { 12 | const sendingKey = await encrypt(authKey); 13 | try { 14 | const response = await axios.post(`${url}/api/auth/google`,{code},{ 15 | headers: { authorization:`Bearer ${sendingKey}` }, 16 | }); 17 | cookies().set({ 18 | name: 'sessionhold', 19 | value: response.data.token, 20 | httpOnly: true, 21 | secure:true, 22 | maxAge:24 * 60 * 60 * 1000 * 7 23 | }) 24 | return {status:response.status,data:response.data} 25 | } catch (error) { 26 | return {status:500,error: 'Internal Server Error' } 27 | } 28 | }; -------------------------------------------------------------------------------- /Server/data/DB.ts: -------------------------------------------------------------------------------- 1 | import { Client } from 'pg'; 2 | import 'dotenv/config'; 3 | 4 | type DetailsType = { 5 | user: string; 6 | password: string; 7 | host: string; 8 | port: number; 9 | database: string; 10 | }; 11 | 12 | const details: DetailsType = { 13 | user: process.env.DB_USER || '', 14 | password: process.env.DB_PASS || '', 15 | host: process.env.DB_HOST || '', 16 | port: parseInt(process.env.DB_PORT || '5432', 10), 17 | database: process.env.DB_NAME || '', 18 | }; 19 | 20 | const client = new Client({ 21 | user: details.user, 22 | password: details.password, 23 | host: details.host, 24 | port: details.port, 25 | database: details.database, 26 | }); 27 | 28 | const connectDB = async () => { 29 | try { 30 | await client.connect(); 31 | console.log('Connected to the database'); 32 | } catch (err:any) { 33 | console.error('Connection error', err.stack); 34 | process.exit(1); // Exit the process with a failure code 35 | } 36 | }; 37 | 38 | export { client, connectDB }; 39 | -------------------------------------------------------------------------------- /Client/app/signed-out/page.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | const page = () => { 4 | return ( 5 |
6 |
7 |
8 |

Signed-Out

9 |

We're working hard to maintain and improve our services, let us know your experience.

10 | Back to Home Page 11 |
12 |
13 |
14 | ) 15 | } 16 | 17 | export default page -------------------------------------------------------------------------------- /Client/components/PolicyPage/Policy.tsx: -------------------------------------------------------------------------------- 1 | import React,{useState} from 'react' 2 | import PrivacyPolicy from './PrivacyPolicy'; 3 | import RefundCancellation from './RefundCancellation'; 4 | import TermsConditions from './TermsConditions'; 5 | 6 | const Policy = () => { 7 | const policyTypes = [ 8 | "Privacy Policy","Terms & Conditions","Refund & Concellation" 9 | ] 10 | const [policyType, setPolicyType] = useState(policyTypes[1]); 11 | return ( 12 |
13 |
14 | {policyTypes.map((each,index)=> 15 | 16 | )} 17 |
18 |
19 | {policyType===policyTypes[0] && } 20 | {policyType===policyTypes[1] && } 21 | {policyType===policyTypes[2] && } 22 |
23 |
24 | ) 25 | } 26 | 27 | export default Policy; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Harmanpreet Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Server/validators/cartCheckoutValidation.ts: -------------------------------------------------------------------------------- 1 | import { checkSchema } from 'express-validator'; 2 | const userIDSchema = checkSchema({ 3 | userID:{ 4 | errorMessage: 'The userID must be provided', 5 | isInt:true, 6 | isLength:{options:{min:1,max:10}}, 7 | notEmpty:true, 8 | isNumeric:true, 9 | trim:true 10 | } 11 | },['body','params']); 12 | const paymentCreationSchema = checkSchema({ 13 | userID:{ 14 | errorMessage: 'The userID must be provided', 15 | isInt:true, 16 | isLength:{options:{min:1,max:10}}, 17 | notEmpty:true, 18 | isNumeric:true, 19 | trim:true 20 | }, 21 | paymentid:{ 22 | errorMessage: 'The paymentID must be provided', 23 | isString:true, 24 | isLength:{options:{min:1,max:255}}, 25 | notEmpty:true, 26 | trim:true 27 | }, 28 | paymentstatus:{ 29 | errorMessage: 'The paymentStatus must be provided correctly', 30 | isString:true, 31 | isLength:{options:{min:1,max:10}}, 32 | notEmpty:true, 33 | trim:true 34 | } 35 | },['body']); 36 | export {userIDSchema,paymentCreationSchema} -------------------------------------------------------------------------------- /Client/components/Orders/NoOrders.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Link from 'next/link' 3 | import Image from 'next/image' 4 | const NoOrders = () => { 5 | return ( 6 |
7 |
8 |
9 |

No Products

10 |

Please Purchase a Product to see your order here

11 | Back to Home Page 12 | guy with checklist and parcels 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default NoOrders -------------------------------------------------------------------------------- /Client/components/Orders/OrderNotFound.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | 4 | const OrderNotFound = () => { 5 | return ( 6 |
7 |
8 |
9 |

404

10 |

Something's missing.

11 |

Sorry, we can't find the order. You'll find lots to explore on the home page or try again later.

12 | Back to Homepage 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default OrderNotFound -------------------------------------------------------------------------------- /Server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ecommerce_backend", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "scripts": { 6 | "build":"tsc", 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "dev": "nodemon index.ts" 9 | }, 10 | "keywords": [], 11 | "author": "", 12 | "license": "ISC", 13 | "description": "", 14 | "dependencies": { 15 | "axios": "^1.7.2", 16 | "bcrypt": "^5.1.1", 17 | "body-parser": "^1.20.2", 18 | "cors": "^2.8.5", 19 | "dotenv": "^16.4.5", 20 | "express": "^4.19.2", 21 | "express-validator": "^7.1.0", 22 | "googleapis": "^140.0.1", 23 | "helmet": "^7.1.0", 24 | "jsonwebtoken": "^9.0.2", 25 | "nodemailer": "^6.9.13", 26 | "pg": "^8.12.0", 27 | "rate-limiter-flexible": "^5.0.3", 28 | "stripe": "^16.2.0" 29 | }, 30 | "devDependencies": { 31 | "@types/express": "^4.17.21", 32 | "@types/node": "^20.14.2", 33 | "@types/bcrypt": "^5.0.2", 34 | "@types/cors": "^2.8.17", 35 | "@types/jsonwebtoken": "^9.0.6", 36 | "@types/nodemailer": "^6.4.15", 37 | "@types/pg": "^8.11.6", 38 | "nodemon": "^3.1.3", 39 | "ts-node": "^10.9.2", 40 | "typescript": "^5.4.5" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Client/components/ProductUi/Product/ProductNotFound.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | 4 | const ProductNotFound = () => { 5 | return ( 6 |
7 |
8 |
9 |

404

10 |

Something's missing.

11 |

Sorry, we can't find that product. You'll find lots to explore on the home page or try again later.

12 | Back to Homepage 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default ProductNotFound -------------------------------------------------------------------------------- /Client/components/Search/NoProduct.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import Link from 'next/link' 4 | const NoProduct = () => { 5 | return ( 6 |
7 |
8 |
9 |

No Products

10 |

We're working hard to Add more products into catalog, you'll find products at other categories or Browse home page.

11 | Back to Home Page 12 | truck with parcels 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default NoProduct -------------------------------------------------------------------------------- /Client/components/Categories/NoProduct.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import Link from 'next/link' 4 | const NoProduct = () => { 5 | return ( 6 |
7 |
8 |
9 |

No Products

10 |

We're working hard to Add more products into catalog, you'll find products at other categories or Browse home page.

11 | Back to Home Page 12 | truck with parcels 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default NoProduct -------------------------------------------------------------------------------- /Client/components/SubCategory/NoProduct.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Image from 'next/image' 3 | import Link from 'next/link' 4 | const NoProduct = () => { 5 | return ( 6 |
7 |
8 |
9 |

No Products

10 |

We're working hard to Add more products into catalog, you'll find products at other categories or Browse home page.

11 | Back to Home Page 12 | truck with parcels 13 |
14 |
15 |
16 | ) 17 | } 18 | 19 | export default NoProduct -------------------------------------------------------------------------------- /Client/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Roboto, Inter, Poppins } from "next/font/google"; 3 | import "./globals.css"; 4 | 5 | // const inter = Inter({ subsets: ["latin"] }); 6 | // const roboto = Roboto({ 7 | // weight: ['400', '700'], 8 | // style: ['normal', 'italic'], 9 | // subsets: ['latin'], 10 | // display: 'swap', 11 | // }) 12 | const poppins = Poppins({ 13 | subsets: ['latin'], 14 | display: 'swap', 15 | variable: '--font-poppins', 16 | weight: ['100', '200', '300', '400', '500', '600', '700', '800', '900'] 17 | }); 18 | export const metadata: Metadata = { 19 | title: "H-Commerce", 20 | description: "Shop At Discount", 21 | }; 22 | 23 | export default function RootLayout({ 24 | children, 25 | }: Readonly<{ 26 | children: React.ReactNode; 27 | }>) { 28 | return ( 29 | 30 | 31 | 32 | 33 | {children} 34 | 35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /Client/components/Orders/NotLoggedin.tsx: -------------------------------------------------------------------------------- 1 | import Link from 'next/link' 2 | import React from 'react' 3 | const NotLoggedin = () => { 4 | return ( 5 |
6 |
7 |
8 |

Sign In

9 |

Please Sign-In to see your order details

10 |

Sorry, we can't show the order details without Login. You'll find lots to explore on the home page without Login or Sign-In Now to see order details.

11 | Sign-In 12 |
13 |
14 |
15 | ) 16 | } 17 | 18 | export default NotLoggedin -------------------------------------------------------------------------------- /Client/app/api/signup.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | import { cookies } from 'next/headers'; 5 | async function encrypt(key:string){ 6 | const encryptedKey = await sign({},key) 7 | return encryptedKey 8 | } 9 | interface propForm{ 10 | userName: string; 11 | email: string; 12 | password: string; 13 | mobile_number: number; 14 | dob: string; 15 | } 16 | 17 | export default async function signUpHandler({ userName, email, password, mobile_number, dob }:propForm,promotional:boolean) { 18 | const url = process.env.BACKEND_URL; 19 | const authKey = process.env.AUTH_KEY as string; 20 | const sendingKey = await encrypt(authKey); 21 | 22 | try { 23 | const response = await axios.post(`${url}/api/user/signup/${promotional}`, { userName, email, password, mobile_number, dob }, { 24 | headers: { authorization:`Bearer ${sendingKey}` }, 25 | }); 26 | cookies().set({ 27 | name: 'sessionhold', 28 | value: response.data.token, 29 | httpOnly: true, 30 | secure:true, 31 | maxAge:24 * 60 * 60 * 1000 * 7 32 | }) 33 | return {status:response.status,data:response.data} 34 | } catch (error) { 35 | return {status:500,error: 'Internal Server Error' } 36 | } 37 | }; -------------------------------------------------------------------------------- /Client/components/Mobile-Interface/Menubar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Sidebar from './Sidebar'; 3 | import { useMenu } from '@/Helpers/MenuContext'; 4 | import { useRouter } from 'next/navigation' 5 | import { HeartIcon,ShoppingBagIcon,Square3Stack3DIcon,HomeIcon,Bars3Icon } from '@heroicons/react/24/outline'; 6 | const Menubar = () => { 7 | const { toggleCart, toggleFav, toggleSidebar, setSidebarType } = useMenu(); 8 | const router = useRouter(); 9 | return ( 10 | <> 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | ) 21 | } 22 | 23 | export default Menubar 24 | // 25 | // onClick={()=>{setSidebar(true);setisMenu(false)}} -------------------------------------------------------------------------------- /Client/components/CommonPage/Common.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import Footer from '@/components/Footer' 3 | import Menubar from '@/components/Mobile-Interface/Menubar' 4 | import Sidebar from '@/components/Mobile-Interface/Sidebar' 5 | import Navbar from '@/components/Navbar' 6 | import { MenuProvider } from '@/Helpers/MenuContext' 7 | import React from 'react' 8 | import Cart from '../ProductUi/Cart' 9 | import Favourite from '../ProductUi/Favourite' 10 | import { AppProvider } from '@/Helpers/AccountDialog' 11 | import { Provider } from 'react-redux' 12 | import { store } from '@/app/store' 13 | import Session from '../Session' 14 | interface ParentComponentProps { 15 | Component: React.ComponentType; 16 | } 17 | const Common: React.FC = ({Component}) => { 18 | return ( 19 | 20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 | 32 | 33 |
34 |
35 | ) 36 | } 37 | 38 | export default Common -------------------------------------------------------------------------------- /Client/app/api/signin.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { cookies } from 'next/headers'; 4 | import { sign } from 'jsonwebtoken'; 5 | async function encrypt(key:string){ 6 | const encryptedKey = await sign({},key) 7 | return encryptedKey 8 | } 9 | export default async function signInHandler({email,password,remember}:{email:string,password:string,remember:boolean}) { 10 | const url = process.env.BACKEND_URL; 11 | const authKey = process.env.AUTH_KEY as string; 12 | const sendingKey = await encrypt(authKey); 13 | 14 | try { 15 | const response = await axios.post(`${url}/api/user/signin/${remember}`, { email, password }, { 16 | headers: { authorization:`Bearer ${sendingKey}` }, 17 | }); 18 | if(remember){ 19 | cookies().set({ 20 | name: 'sessionhold', 21 | value: response.data.token, 22 | httpOnly: true, 23 | secure:true, 24 | maxAge:24 * 60 * 60 * 1000 * 7 25 | }) 26 | }else{ 27 | cookies().set({ 28 | name: 'sessionhold', 29 | value: response.data.token, 30 | httpOnly: true, 31 | secure:true, 32 | maxAge:24 * 60 * 60 * 1000 33 | }) 34 | } 35 | return {status:response.status,data:response.data} 36 | } catch (error) { 37 | return {status:500,error: 'Internal Server Error' } 38 | } 39 | }; -------------------------------------------------------------------------------- /Client/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Client/components/DropdownMenu/Product.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | interface option{ 3 | title: string, 4 | link: string, 5 | } 6 | const Product = ({ options }:{options:option[]}) => { 7 | const [margin, setMargin] = useState(10); 8 | const [opacity, setOpacity] = useState(0); 9 | 10 | useEffect(() => { 11 | const timer = setTimeout(() => { 12 | setMargin(0); 13 | setOpacity(1); 14 | }, 5); 15 | 16 | // Cleanup function to clear the timeout if the component unmounts 17 | return () => clearTimeout(timer); 18 | }, []); 19 | 20 | return ( 21 | 40 | ); 41 | }; 42 | 43 | export default Product; 44 | -------------------------------------------------------------------------------- /Client/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e-commerce", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint", 10 | "devbackend": "nodemon ../Server/index.ts" 11 | }, 12 | "dependencies": { 13 | "@headlessui/react": "^2.0.4", 14 | "@heroicons/react": "^2.1.3", 15 | "@rcaferati/wac": "^1.0.0", 16 | "@react-oauth/google": "^0.12.1", 17 | "@reduxjs/toolkit": "^2.2.5", 18 | "@stripe/react-stripe-js": "^2.7.3", 19 | "@stripe/stripe-js": "^4.1.0", 20 | "axios": "^1.7.2", 21 | "framer-motion": "^11.2.10", 22 | "jsonwebtoken": "^9.0.2", 23 | "next": "^14.2.5", 24 | "react": "^18", 25 | "react-awesome-slider": "^4.1.0", 26 | "react-dom": "^18", 27 | "react-otp-input": "^3.1.1", 28 | "react-redux": "^9.1.2", 29 | "react-stars": "^2.2.5", 30 | "sharp": "^0.33.4" 31 | }, 32 | "devDependencies": { 33 | "@types/react": "^18", 34 | "@types/react-dom": "^18", 35 | "postcss": "^8", 36 | "tailwindcss": "^3.4.1", 37 | "ts-node": "^10.9.2", 38 | "typescript": "^5", 39 | "@types/js-cookie": "^3.0.6", 40 | "@types/jsonwebtoken": "^9.0.6", 41 | "@types/react-redux": "^7.1.33", 42 | "@types/react-stars": "^2.2.4", 43 | "prop-types": "^15.8.1", 44 | "@tailwindcss/aspect-ratio": "^0.4.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Server/validators/userOTPValidation.ts: -------------------------------------------------------------------------------- 1 | import { checkSchema } from "express-validator"; 2 | 3 | const EmailSchema = checkSchema({ 4 | email: { 5 | errorMessage: 'The email must be at least 5 characters', 6 | escape:true, 7 | isLength: { options: { min: 5,max:128 } }, 8 | isEmail:{bail:true}, 9 | matches: { options: /[@]/ }, 10 | notEmpty:true, 11 | isString:true, 12 | trim:true 13 | }, 14 | }); 15 | const EmailPasswordOtpSchema = checkSchema({ 16 | email: { 17 | errorMessage: 'The email must be at least 5 characters', 18 | escape:true, 19 | isLength: { options: { min: 5,max:128 } }, 20 | isEmail:{bail:true}, 21 | matches: { options: /[@]/ }, 22 | notEmpty:true, 23 | isString:true, 24 | trim:true 25 | }, 26 | password: { 27 | errorMessage: 'The password must be at least 8 characters', 28 | isLength: { options: { min: 8,max:32 } }, 29 | escape:true, 30 | notEmpty:true, 31 | isString:true, 32 | trim:true 33 | }, 34 | otp: { 35 | in: ['body'], 36 | isString:true, 37 | isLength: { 38 | options: { min: 4, max: 4 }, 39 | errorMessage: 'OTP must be exactly 4 digits' 40 | }, 41 | errorMessage: 'OTP must be a 4-digit integer' 42 | } 43 | }); 44 | 45 | export {EmailSchema,EmailPasswordOtpSchema}; -------------------------------------------------------------------------------- /Client/app/App.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | import React from 'react' 3 | import Navbar from '@/components/Navbar' 4 | import Trends from '@/components/Trends' 5 | import Status from '@/components/Status' 6 | import Footer from '@/components/Footer' 7 | import Details from '@/components/Details' 8 | import Tabs from '@/components/Tabs' 9 | import Menubar from '@/components/Mobile-Interface/Menubar' 10 | import { Provider } from 'react-redux' 11 | import Cart from '@/components/ProductUi/Cart' 12 | import Favourite from '@/components/ProductUi/Favourite' 13 | import { MenuProvider } from '@/Helpers/MenuContext' 14 | import Banner from '@/components/Banner' 15 | import { AppProvider } from '@/Helpers/AccountDialog' 16 | import { store } from './store' 17 | import Session from '@/components/Session' 18 | const App = () => { 19 | 20 | return ( 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 | ) 41 | } 42 | 43 | export default App -------------------------------------------------------------------------------- /Client/components/Orders/Dropdown.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from 'react'; 2 | interface option{ 3 | title: string, 4 | link: string, 5 | } 6 | const Dropdown = ({ options,orderid }:{options:option[],orderid:number}) => { 7 | const [margin, setMargin] = useState(10); 8 | const [opacity, setOpacity] = useState(0); 9 | 10 | useEffect(() => { 11 | const timer = setTimeout(() => { 12 | setMargin(0); 13 | setOpacity(1); 14 | }, 5); 15 | 16 | // Cleanup function to clear the timeout if the component unmounts 17 | return () => clearTimeout(timer); 18 | }, []); 19 | 20 | return ( 21 | 40 | ); 41 | }; 42 | 43 | export default Dropdown; 44 | -------------------------------------------------------------------------------- /Client/components/Trends.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { topCat } from '@/app/data'; 3 | 4 | const Trends = () => { 5 | return ( 6 |
7 |
8 | {topCat.map((Cat,index)=> 9 |
10 |
11 |
12 | 13 |
14 |
15 |

{Cat.name}

16 | Show All 17 |
18 |
19 |
20 |

({Cat.quantity})

21 |
22 |
23 | )} 24 |
25 |
26 | ) 27 | } 28 | 29 | export default Trends -------------------------------------------------------------------------------- /Server/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Express, Request, Response } from 'express'; 2 | import bodyParser from 'body-parser'; 3 | import cors from 'cors'; 4 | import routes from './routes'; 5 | import 'dotenv/config'; 6 | import helmet from 'helmet'; 7 | import { connectDB } from './data/DB'; // Import the connectDB function 8 | import rateLimiterMiddleware from './middleware/rateLimit'; 9 | import authenticateToken from './middleware/header_auth'; 10 | const app: Express = express(); 11 | app.set('trust proxy', true); 12 | const port = process.env.PORT || 3500; 13 | 14 | 15 | app.use(rateLimiterMiddleware); 16 | app.use(bodyParser.json({limit:'100kb'})); 17 | app.use(helmet()); 18 | const origin_url:string = process.env.FRONTEND_SERVER_ORIGIN as string; 19 | const corsOptions = { 20 | origin: origin_url, 21 | methods: 'GET,HEAD,PUT,PATCH,POST,DELETE', 22 | credentials: true, // Enable cookies and authentication headers (if needed) 23 | }; 24 | app.use(express.urlencoded({ extended: false })); 25 | app.use(cors(corsOptions)); 26 | app.use(authenticateToken); 27 | app.get('/', (req: Request, res: Response) => { 28 | res.status(200).json({ message: 'Success' }); 29 | }); 30 | app.use('/api', routes); 31 | // Function to start the server 32 | const startServer = async () => {// Ensure the database connection is established 33 | app.listen(port, () => { 34 | console.log(`[server]: Server is running at Port ${port}`); 35 | }); 36 | await connectDB(); 37 | }; 38 | 39 | startServer(); 40 | -------------------------------------------------------------------------------- /Client/app/api/filter.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | const url = process.env.BACKEND_URL; 9 | const authKey = process.env.AUTH_KEY as string; 10 | export async function categoryFilterHandler({minPrice,maxPrice,categoryID,minRating,categoryName}:{minPrice:number,maxPrice:number,categoryID:number,minRating:number,categoryName:string|string[]}) { 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/filter/category/${minPrice}/${maxPrice}/${categoryID}/${minRating}/${categoryName}`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; 21 | export async function categoryOnlyFilterHandler({categoryID,categoryName}:{categoryID:number,categoryName:string|string[]}) { 22 | const sendingKey = await encrypt(authKey); 23 | try { 24 | const response = await axios.get(`${url}/api/filter/category-only/${categoryID}/${categoryName}`,{ 25 | headers: { authorization:`Bearer ${sendingKey}` }, 26 | }); 27 | return {status:response.status,data:response.data} 28 | } catch (error) { 29 | return {status:500,error: 'Internal Server Error' } 30 | } 31 | }; -------------------------------------------------------------------------------- /Client/app/api/search.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | export default async function searchProductHandler({productName}:{productName:string|string[]}) { 9 | const url = process.env.BACKEND_URL; 10 | const authKey = process.env.AUTH_KEY as string; 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/search/product/${productName}`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; 21 | export async function searchFilteredHandler({productName,minPrice,maxPrice,rating}:{productName:string|string[],minPrice:number,maxPrice:number,rating:number}) { 22 | const url = process.env.BACKEND_URL; 23 | const authKey = process.env.AUTH_KEY as string; 24 | const sendingKey = await encrypt(authKey); 25 | try { 26 | const response = await axios.get(`${url}/api/search/filtered-product/${productName}/${minPrice}/${maxPrice}/${rating}`,{ 27 | headers: { authorization:`Bearer ${sendingKey}` }, 28 | }); 29 | return {status:response.status,data:response.data} 30 | } catch (error) { 31 | return {status:500,error: 'Internal Server Error' } 32 | } 33 | }; -------------------------------------------------------------------------------- /Client/app/api/subCategory.ts: -------------------------------------------------------------------------------- 1 | "use server" 2 | import axios from 'axios'; 3 | import { sign } from 'jsonwebtoken'; 4 | async function encrypt(key:string){ 5 | const encryptedKey = await sign({},key) 6 | return encryptedKey 7 | } 8 | const url = process.env.BACKEND_URL; 9 | const authKey = process.env.AUTH_KEY as string; 10 | export default async function subCategoryDataHandler(mainCategory:string | string[],subCategory:string | string[]) { 11 | const sendingKey = await encrypt(authKey); 12 | try { 13 | const response = await axios.get(`${url}/api/sub-category/${mainCategory}/${subCategory}`,{ 14 | headers: { authorization:`Bearer ${sendingKey}` }, 15 | }); 16 | return {status:response.status,data:response.data} 17 | } catch (error) { 18 | return {status:500,error: 'Internal Server Error' } 19 | } 20 | }; 21 | export async function subCategoryFilteredHandler({categoryID,minPrice,maxPrice,rating}:{categoryID:number,minPrice:number,maxPrice:number,rating:number}) { 22 | const url = process.env.BACKEND_URL; 23 | const authKey = process.env.AUTH_KEY as string; 24 | const sendingKey = await encrypt(authKey); 25 | try { 26 | const response = await axios.get(`${url}/api/sub-category/filtered-product/${categoryID}/${minPrice}/${maxPrice}/${rating}`,{ 27 | headers: { authorization:`Bearer ${sendingKey}` }, 28 | }); 29 | return {status:response.status,data:response.data} 30 | } catch (error) { 31 | return {status:500,error: 'Internal Server Error' } 32 | } 33 | }; -------------------------------------------------------------------------------- /Client/components/Login/otpTimer.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | // Define the type for the time state 4 | interface Time { 5 | minutes: number; 6 | seconds: number; 7 | } 8 | 9 | // Define the props type 10 | interface CountdownProps { 11 | onComplete: () => void; 12 | } 13 | 14 | const Countdown: React.FC = ({ onComplete }) => { 15 | // Initialize state for minutes and seconds 16 | const [time, setTime] = useState