[
  {
    "path": ".gitignore",
    "content": "# dependencies\nnode_modules/\n\n# environment variables\n.env\ntoDO.txt\n"
  },
  {
    "path": "jsconfig.json",
    "content": "{\n    \"compilerOptions\": {\n        \"module\": \"NodeNext\",\n        \"moduleResolution\": \"NodeNext\",\n        \"paths\": {\n            \"#repositories/*\": [\"./src/repositories/*\"],\n            \"#controllers/*\": [\"./src/controllers/*\"],\n            \"#db/*\": [\"./src/db/*\"],\n            \"#middlewares/*\": [\"./src/middlewares/*\"],\n            \"#routes/*\": [\"./src/routes/*\"],\n            \"#schemas/*\": [\"./src/schemas/*\"],\n            \"#services/*\": [\"./src/services/*\"],\n            \"#utils/*\": [\"./src/utils/*\"],\n            \"#src/*\": [\"./src/*\"]\n        }\n    }\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n    \"name\": \"e-commerce\",\n    \"version\": \"1.0.0\",\n    \"description\": \"\",\n    \"homepage\": \"https://github.com/MazenShoaip/e-commerce#readme\",\n    \"bugs\": {\n        \"url\": \"https://github.com/MazenShoaip/e-commerce/issues\"\n    },\n    \"repository\": {\n        \"type\": \"git\",\n        \"url\": \"git+https://github.com/MazenShoaip/e-commerce.git\"\n    },\n    \"license\": \"ISC\",\n    \"author\": \"\",\n    \"type\": \"module\",\n    \"main\": \"server.js\",\n    \"scripts\": {\n        \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n    },\n    \"dependencies\": {\n        \"bcrypt\": \"^6.0.0\",\n        \"cookie-parser\": \"^1.4.7\",\n        \"dotenv\": \"^17.4.2\",\n        \"express\": \"^5.2.1\",\n        \"express-rate-limit\": \"^8.5.2\",\n        \"jsonwebtoken\": \"^9.0.3\",\n        \"nodemailer\": \"^8.0.7\",\n        \"pg\": \"^8.20.0\",\n        \"pg-format\": \"^1.0.4\",\n        \"smtp-server\": \"^3.18.4\",\n        \"zod\": \"^4.4.1\"\n    },\n    \"imports\": {\n        \"#repositories/*\": \"./src/repositories/*\",\n        \"#controllers/*\": \"./src/controllers/*\",\n        \"#db/*\": \"./src/db/*\",\n        \"#middlewares/*\": \"./src/middlewares/*\",\n        \"#routes/*\": \"./src/routes/*\",\n        \"#schemas/*\": \"./src/schemas/*\",\n        \"#services/*\": \"./src/services/*\",\n        \"#utils/*\": \"./src/utils/*\",\n        \"#src/*\": \"./src/*\"\n    }\n}\n"
  },
  {
    "path": "server.js",
    "content": "// process.on(\"uncaughtException\", (err) => {\n//     console.error(\"UNCAUGHT EXCEPTION 💥:\", err);\n//     process.exit(1);\n// });\n\nimport app from \"#src/app.js\";\nimport AppError from \"#utils/appError.js\";\nasync function startServer() {\n    try {\n        app.listen(5000);\n        console.log(\"Server is Ready\");\n    } catch (err) {\n        console.log(\"Server is not Ready\");\n        throw new AppError(\"Server Error\", 500);\n        // process.exit(1);\n    }\n}\n\nawait startServer();\n\n// process.on(\"unhandledRejection\", (err) => {\n//     console.error(\"UNHANDLED REJECTION 💥:\", err);\n//     process.exit(1);\n// });\n"
  },
  {
    "path": "src/app.js",
    "content": "import express from \"express\";\nimport cookieParser from \"cookie-parser\";\nimport errorsHandle from \"#utils/errorsHandle.js\";\nimport notFound from \"#controllers/notFound.js\";\nimport router from \"#routes/router.js\";\nconst app = express();\n\napp.use(express.json());\napp.use(cookieParser());\n\napp.use('/', router)\napp.use(notFound);\napp.use(errorsHandle);\nexport default app;\n"
  },
  {
    "path": "src/controllers/authController.js",
    "content": "import signupService from \"#services/auth/signupService.js\";\nimport loginService from \"#services/auth/loginService.js\";\nimport refreshTokenService from \"#services/auth/refreshTokenService.js\";\nimport refreshVerifyEmailService from \"#services/auth/refreshVerifyEmailService.js\";\nimport verifyEmailService from \"#services/auth/verifyEmailService.js\";\n\nexport async function loginController(req, res, next) {\n    let result = await loginService(req.body, res);\n    res.status(200).json({ success: true, ...result });\n}\n\nexport async function signupController(req, res, next) {\n    let result = await signupService(req.body);\n    res.status(201).json({ success: true, ...result });\n}\n\nexport async function verifyEmailController(req, res, next) {\n    let result = await verifyEmailService(req.body);\n    res.status(201).json({ success: true, ...result });\n}\n\nexport async function refreshVerifyEmailController(req, res, next) {\n    let result = await refreshVerifyEmailService(req.body);\n    res.status(201).json({ success: true, ...result });\n}\n\nexport async function refreshTokenController(req, res, next) {\n    let result = await refreshTokenService(req.cookies.refreshToken, res);\n    res.status(200).json({ success: true, ...result });\n}\n"
  },
  {
    "path": "src/controllers/cartController.js",
    "content": "import setCartService from \"#services/cart/setCartService.js\";\nimport getCartService from \"#services/cart/getCartService.js\";\n\nexport async function setCartController(req, res, next) {\n    let result = await setCartService(req.body, req.user);\n    res.status(201).json({ success: true, ...result });\n}\n\nexport async function getCartController(req, res, next) {\n    let result = await getCartService(req.user);\n    res.status(200).json({ success: true, result });\n}\n"
  },
  {
    "path": "src/controllers/notFound.js",
    "content": "import AppError from \"#utils/appError.js\";\n\nexport default function notFound(req, res, next) {\n    next(new AppError(\"Route was not found\", 404));\n}\n"
  },
  {
    "path": "src/controllers/orderController.js",
    "content": "import addOrderService from \"#services/order/addOrderService.js\";\nimport getOrderService from \"#services/order/getOrderService.js\";\n\nexport async function addOrderController(req, res, next) {\n    let result = await addOrderService(req.body, req.user);\n    res.status(201).json({ success: true, ...result });\n}\n\nexport async function getOrderController(req, res, next) {\n    let result = await getOrderService(req.user);\n    res.status(200).json({ success: true, result });\n}\n"
  },
  {
    "path": "src/controllers/productController.js",
    "content": "import addProductService from \"#services/product/addProductService.js\";\nimport updateProductService from \"#services/product/updateProductService.js\";\nimport getProductsService from \"#services/product/getProductsService.js\";\nimport removeProductService from \"#services/product/deleteProductService.js\";\nimport AppError from \"#utils/appError.js\";\n\nexport async function addProductController(req, res, next) {\n    if (req.user.role !== \"admin\") throw new AppError(\"Access Denied\", 401);\n    let result = await addProductService(req.body);\n    res.status(201).json({ success: true, ...result });\n}\nexport async function removeProductController(req, res, next) {\n    if (req.user.role !== \"admin\") throw new AppError(\"Access Denied\", 401);\n    let result = await removeProductService(req.body);\n    if (result.result === 0) throw new AppError(\"Item was not found\", 400);\n    res.status(200).json({ success: true, ...result });\n}\nexport async function updateProductController(req, res, next) {\n    if (req.user.role !== \"admin\") throw new AppError(\"Access Denied\", 401);\n    let result = await updateProductService(req.body);\n    if (result.matchedCount === 0)\n        throw new AppError(\"Item was not found\", 400);\n    res.status(200).json({ success: true, result });\n}\nexport async function getProductsController(req, res, next) {\n    let result = await getProductsService();\n    res.status(200).json({ success: true, result });\n}\n"
  },
  {
    "path": "src/database/database.js",
    "content": "import { configDotenv } from \"dotenv\";\nimport { Pool } from \"pg\";\nconfigDotenv();\nconst pool = new Pool({\n    connectionString: process.env.DATABASE_URL,\n});\n\nexport default pool;"
  },
  {
    "path": "src/db/db.js",
    "content": "import { MongoClient } from \"mongodb\";\nimport { configDotenv } from \"dotenv\";\nimport AppError from \"#utils/appError.js\";\nconfigDotenv();\nlet db;\nexport default async function connectDB() {\n    const client = new MongoClient(process.env.MONGO_URI);\n    await client.connect();\n    // await client.db(\"ecommerce\").dropDatabase();\n    db = client.db(\"ecommerce\");\n    await db\n        .collection(\"pendingUsers\")\n        .createIndex({ createdAt: 1 }, { expireAfterSeconds: 300 });\n    await db\n        .collection(\"refreshTokens\")\n        .createIndex(\n            { createdAt: 1 },\n            { expireAfterSeconds: 7 * 24 * 60 * 60 },\n        );\n    console.log(\"MongoDB connected\");\n    return db;\n}\n\nexport function getDB()\n{\n    if (!db)\n        // db = await connectDB()\n        throw new AppError(\"Database Error\", 500);\n    return db\n}"
  },
  {
    "path": "src/middlewares/verifyToken.js",
    "content": "import { configDotenv } from \"dotenv\";\nimport jwt from \"jsonwebtoken\";\nimport AppError from \"#utils/appError.js\";\nconfigDotenv();\nexport default function verifyToken(req, res, next) {\n    let token = String(req.headers.authorization).split(\" \")[1];\n    let user;\n    try {\n        user = jwt.verify(token, process.env.ACCESS_JWT_KEY);\n    } catch (err) {\n        throw new AppError(\"Access Denied\", 401);\n    }\n    if (user.type !== \"access\") throw AppError(\"Access Denied\", 401);\n    req.user = user;\n    next();\n}\n"
  },
  {
    "path": "src/repositories/databaseRepository.js",
    "content": "import pool from \"#src/database/database.js\";\nimport AppError from \"#utils/appError.js\";\nimport format from \"pg-format\";\n\nexport async function addItem(data, relation) {\n    let keys = Object.keys(data);\n    let values = Object.values(data);\n    let query = format(\n        \"INSERT INTO %I (%I) VALUES (%L) RETURNING id\",\n        relation,\n        keys,\n        values,\n    );\n    let result = await pool.query(query);\n    return result;\n}\nexport async function addItems(data, relation, chunkSize = 1000) {\n    let keys = Object.keys(data[0]);\n    let results = [];\n    for (let i = 0; i < data.length; i += chunkSize) {\n        let chunk = data.slice(i, i + chunkSize);\n        let values = chunk.map((obj) => keys.map((k) => obj[k]));\n\n        let query = format(\n            \"INSERT INTO %I (%I) VALUES %L RETURNING id\",\n            relation,\n            keys,\n            values,\n        );\n        let result = await pool.query(query);\n        results.push(result.rows);\n    }\n    return results;\n}\n\nexport async function findItem(data, relation, limit = \"ALL\", common = true) {\n    let keys = Object.keys(data);\n    let values = Object.values(data);\n    if (keys.length === 0)\n        return (await pool.query(format(\"SELECT * FROM %I\", relation))).rows;\n    if (limit !== \"ALL\" && isNaN(limit))\n        throw new AppError(\"Limit must be a number\", 500);\n    let conditions = keys\n        .map((k, i) => {\n            return format(\"%I = %L\", k, values[i]);\n        })\n        .join(` ${common ? \"AND\" : \"OR\"} `);\n    let query = format(\n        \"SELECT * FROM %I WHERE (%s) LIMIT %s\",\n        relation,\n        conditions,\n        limit,\n    );\n    let result = await pool.query(query);\n    return result;\n}\n\nexport async function updateItem(data, filters, relation, common = true) {\n    const dataKeys = Object.keys(data);\n    const dataValues = Object.values(data);\n    const setClause = dataKeys\n        .map((k, i) => format(\"%I = %L\", k, dataValues[i]))\n        .join(\", \");\n\n    const filterKeys = Object.keys(filters);\n    const filterValues = Object.values(filters);\n\n    if (filterKeys.length === 0) {\n        throw new AppError(\n            \"You must provide at least one filter to update a row safely.\",\n            500,\n        );\n    }\n\n    const whereClause = filterKeys\n        .map((k, i) => format(\"%I = %L\", k, filterValues[i]))\n        .join(` ${common ? \"AND\" : \"OR\"} `);\n    let query = format(\n        \"UPDATE %I SET %s WHERE %s\",\n        relation,\n        setClause,\n        whereClause,\n    );\n    let result = await pool.query(query);\n    return result;\n}\nexport async function deleteItem(filters, relation, common = true) {\n    const filterKeys = Object.keys(filters);\n    const filterValues = Object.values(filters);\n\n    if (filterKeys.length === 0) {\n        throw new AppError(\n            \"You must provide at least one filter to delete a row safely.\",\n            500,\n        );\n    }\n\n    const whereClause = filterKeys\n        .map((k, i) => format(\"%I = %L\", k, filterValues[i]))\n        .join(` ${common ? \"AND\" : \"OR\"} `);\n    let query = format(\"DELETE FROM %I WHERE %s\", relation, whereClause);\n    let result = await pool.query(query);\n    return result;\n}\n\nexport async function setCart(data) {\n    let items = data.items;\n    let user_id = data.user_id;\n    for (let i of items) {\n        if (!i.quantity) {\n            await deleteItem({ product_id: i.product_id, user_id }, \"carts\");\n            continue;\n        }\n        let item = (\n            await findItem({ product_id: i.product_id, user_id }, \"carts\")\n        ).rows[0];\n        if (!item) {\n            await addItem({ user_id, ...i }, \"carts\");\n            continue;\n        }\n        await updateItem(\n            { quantity: i.quantity },\n            { id: item.id, user_id },\n            \"carts\",\n        );\n    }\n}\nexport async function setOrder(data) {\n    let items = data.items;\n    let user_id = data.user_id;\n    let address = data.address;\n    let verified_items = [];\n    let stocks = [];\n    let balance = 0;\n    let order_id = (await addItem({ user_id, address, balance }, \"orders\"))\n        .rows[0];\n    for (let i of items) {\n        let itemDB = (await findItem({ id: i.product_id }, \"products\")).rows[0];\n        if (itemDB.stock < i.quantity) {\n            await deleteItem({ id: order_id.id }, \"orders\");\n            throw new AppError(\"Not enough stock of \" + itemDB.product, 400);\n        }\n        balance += i.quantity * itemDB.price;\n        verified_items.push({\n            product_id: i.product_id,\n            order_id: order_id.id,\n            quantity: i.quantity,\n        });\n        stocks.push({\n            product_id: i.product_id,\n            stock: itemDB.stock - i.quantity,\n        });\n    }\n    for (let i of stocks) {\n        await updateItem({ stock: i.stock }, { id: i.product_id }, \"products\");\n    }\n    addItems(verified_items, \"orders_items\");\n    await updateItem({ balance }, { id: order_id.id }, \"orders\");\n    await deleteItem({ user_id }, \"carts\");\n}\n"
  },
  {
    "path": "src/repositories/userRepository.js",
    "content": "import pool from \"#src/database/database.js\";\n\nasync function getUsers() {\n    return pool.query(\"SELECT * FROM users limit 1\");\n}\n\nasync function getUser(info) {\n    return pool.query(\"SELECT * FROM users \");\n}\nconsole.log((await getUsers()).rows)"
  },
  {
    "path": "src/routes/authRouter.js",
    "content": "import express from \"express\";\nimport * as authController from \"#controllers/authController.js\";\nimport setLimit from \"#utils/rateLimit.js\";\n\nconst authRoute = express.Router();\nauthRoute.post(\"/login\", setLimit(10, 1), authController.loginController);\nauthRoute.post(\"/signup\",setLimit(10, 10), authController.signupController);\nauthRoute.post(\"/otp/email\",setLimit(5, 1), authController.verifyEmailController);\nauthRoute.post(\n    \"/otp/refresh/email\",\n    authController.refreshVerifyEmailController,\n);\nauthRoute.post(\"/token/refresh\",setLimit(3, 1), authController.refreshTokenController);\n\nexport default authRoute;\n"
  },
  {
    "path": "src/routes/cartRoute.js",
    "content": "import express from \"express\";\nimport * as cartController from \"#controllers/cartController.js\";\nimport verifyToken from \"#middlewares/verifyToken.js\";\n\nconst cartRoute = express.Router();\ncartRoute.post(\"/add\", verifyToken, cartController.setCartController);\n// cartRoute.delete(\"/delete\", verifyToken, cartController.removeCartController);\n// cartRoute.patch(\"/update\", verifyToken, cartController.updateCartController);\ncartRoute.get(\"/\", verifyToken, cartController.getCartController);\n\nexport default cartRoute;\n"
  },
  {
    "path": "src/routes/orderRoute.js",
    "content": "import express from \"express\";\nimport * as orderController from \"#controllers/orderController.js\";\nimport verifyToken from \"#middlewares/verifyToken.js\";\n\nconst orderRoute = express.Router();\norderRoute.post(\"/add\", verifyToken, orderController.addOrderController);\norderRoute.get(\"/\", verifyToken, orderController.getOrderController);\n\nexport default orderRoute;\n"
  },
  {
    "path": "src/routes/productRoute.js",
    "content": "import express from \"express\";\nimport * as productController from \"#controllers/productController.js\";\nimport verifyToken from \"#middlewares/verifyToken.js\";\n\nconst productRoute = express.Router();\nproductRoute.post(\"/add\", verifyToken, productController.addProductController);\nproductRoute.delete(\n    \"/delete\",\n    verifyToken,\n    productController.removeProductController,\n);\nproductRoute.patch(\n    \"/update\",\n    verifyToken,\n    productController.updateProductController,\n);\nproductRoute.get(\"/products\", productController.getProductsController);\n\nexport default productRoute;\n"
  },
  {
    "path": "src/routes/router.js",
    "content": "import express from 'express'\nimport authRoute from '#routes/authRouter.js'\nimport productRoute from \"#routes/productRoute.js\";\nimport cartRoute from '#routes/cartRoute.js';\nimport setLimit from \"#utils/rateLimit.js\";\nimport orderRoute from './orderRoute.js';\n\nlet router = express.Router()\n\nrouter.use(setLimit(100, 1))\n\nrouter.use('/auth', authRoute)\nrouter.use('/product', productRoute)\nrouter.use('/cart', cartRoute)\nrouter.use('/order', orderRoute)\n\nexport default router"
  },
  {
    "path": "src/schemas/cartSchema.js",
    "content": "import { z } from \"zod\";\nlet cartElement = z.object({\n    product_id: z.number().int().min(0),\n    quantity: z.number().int().min(0),\n});\nlet cartSchema = z.object({\n    cart: z.array(cartElement).min(1),\n});\nexport default cartSchema;\n"
  },
  {
    "path": "src/schemas/idSchema.js",
    "content": "import { z } from \"zod\";\nlet idSchema = z.object({\n    id: z.string().min(1),\n});\n\nexport default idSchema;\n"
  },
  {
    "path": "src/schemas/productSchema.js",
    "content": "import { z } from \"zod\";\nlet productSchema = z.object({\n    product: z.string().min(3),\n    stock: z.int().min(0),\n    price: z.number().min(0)\n\n});\n\nexport default productSchema;\n"
  },
  {
    "path": "src/schemas/productUpdateSchema.js",
    "content": "import { z } from \"zod\";\nlet productUpdateSchema = z.object({\n    id: z.string().min(1),\n    product: z.string().min(3).optional(),\n    stock: z.int().min(0).optional(),\n    price: z.number().min(0).optional()\n\n});\n\nexport default productUpdateSchema;\n"
  },
  {
    "path": "src/schemas/userLoginSchema.js",
    "content": "import { z } from \"zod\";\nlet userLoginSchema = z.object({\n    username: z.string().min(3).optional(),\n    email: z.email(\"invalid email\").optional(),\n    password: z.string().min(8),\n});\n\nexport default userLoginSchema;\n"
  },
  {
    "path": "src/schemas/userSignupSchema.js",
    "content": "import {  z } from \"zod\";\nlet userSignupSchema = z.object({\n    username: z.string().min(3),\n    password: z.string().min(8),\n    email: z.email(\"invalid email\"),\n});\n\nexport default userSignupSchema;\n"
  },
  {
    "path": "src/schemas/verifyEmailSchema.js",
    "content": "import { z } from \"zod\";\nlet verifyEmailSchema = z.object({\n    id: z.string().min(1),\n    otp: z.string().length(7),\n});\n\nexport default verifyEmailSchema;\n"
  },
  {
    "path": "src/services/auth/loginService.js",
    "content": "import userLoginSchema from \"#schemas/userLoginSchema.js\";\nimport AppError from \"#utils/appError.js\";\nimport bcrypt from \"bcrypt\";\nimport generateToken from \"#utils/generateToken.js\";\nimport storeRefreshToken from \"#utils/storeRefreshToken.js\";\nimport { findItem } from \"#repositories/databaseRepository.js\";\nexport default async function loginService(body, res) {\n    let verify = userLoginSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n\n    let data = verify.data;\n    if (!data.username && !data.email) throw new AppError(\"Invalid Data\", 400);\n\n    let id = data.email || data.username;\n\n    let user = (await findItem({ username: id, email: id }, \"users\", 'ALL', false))\n        .rows[0];\n    if (!user) throw new AppError(\"User is not found\", 404);\n    let passwordMatch = await bcrypt.compare(data.password, user.password);\n    if (!passwordMatch) throw new AppError(\"Invalid email or password\", 401);\n    let accessToken = await generateToken(\n        { sub: user.id, role: user.role },\n        \"access\",\n        \"1d\",\n    );\n\n    let refreshToken = await generateToken(\n        { sub: user.id, role: user.role },\n        \"refresh\",\n        \"1d\",\n    );\n    storeRefreshToken(refreshToken, res);\n    return { accessToken };\n}\n"
  },
  {
    "path": "src/services/auth/refreshTokenService.js",
    "content": "import { configDotenv } from \"dotenv\";\nimport { deleteItem } from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\nimport generateToken from \"#utils/generateToken.js\";\nimport storeRefreshToken from \"#utils/storeRefreshToken.js\";\nimport jwt from \"jsonwebtoken\";\nconfigDotenv();\n\nexport default async function refreshTokenService(token, res) {\n    let user;\n    try {\n        user = jwt.verify(token, process.env.REFRESH_JWT_KEY);\n    } catch (err) {\n        throw new AppError(\"Access Denied\", 401);\n    }\n    if (user.type !== \"refresh\") throw new AppError(\"Access Denied\", 401);\n    let tokenUser = (await deleteItem({ jti: user.jti }, \"tokens\")).rowCount;\n    if (!tokenUser) throw new AppError(\"Access Denied\", 401);\n    let accessToken = await generateToken(\n        { sub: user.sub, role: user.role },\n        \"access\",\n        \"15m\",\n    );\n\n    let refreshToken = await generateToken(\n        { sub: user.sub, role: user.role },\n        \"refresh\",\n        \"1d\",\n    );\n    storeRefreshToken(refreshToken, res);\n    return { success: true, accessToken };\n}\n"
  },
  {
    "path": "src/services/auth/refreshVerifyEmailService.js",
    "content": "import idSchema from \"#schemas/idSchema.js\";\nimport AppError from \"#utils/appError.js\";\nimport bcrypt from \"bcrypt\";\nimport generateOTP from \"#utils/generateOTP.js\";\nimport sendEmail from \"#services/emailService.js\";\nimport { findItem, updateItem } from \"#repositories/databaseRepository.js\";\nexport default async function refreshVerifyEmailService(body) {\n    let verify = idSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = verify.data;\n    let user = (await findItem({ id: data.id }, \"pending_users\")).rows[0];\n    if (!user) throw new AppError(\"ID was not found\", 400);\n    let otp = generateOTP();\n    let hashedOTP = await bcrypt.hash(String(otp), 10);\n    await updateItem(\n        { otp: hashedOTP, created_at: new Date().toISOString() },\n        { id: data.id },\n        \"pending_users\",\n    );\n    await sendEmail({\n        to: user.email,\n        subject: \"otp code\",\n        text: `Your otp is ${otp}`,\n    });\n    return { success: true, message: \"OTP refresh message is sent\" };\n}\n"
  },
  {
    "path": "src/services/auth/signupService.js",
    "content": "import userSignupSchema from \"#schemas/userSignupSchema.js\";\nimport AppError from \"#utils/appError.js\";\nimport bcrypt from \"bcrypt\";\nimport sendEmail from \"#services/emailService.js\";\nimport generateOTP from \"#utils/generateOTP.js\";\nimport { addItem, findItem } from \"#repositories/databaseRepository.js\";\n\nexport default async function signupService(body) {\n    let verify = userSignupSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = verify.data;\n    let foundUsers = (\n        await findItem(\n            { username: data.username, email: data.email },\n            \"users\",\n            1,\n            false,\n        )\n    ).rows[0];\n    if (foundUsers) {\n        let err = foundUsers.username == data.username ? \"Username\" : \"Email\";\n        throw new AppError(err, \"is already used\", 409);\n    }\n\n    let password = await bcrypt.hash(data.password, 10);\n    let otpCode = generateOTP();\n    let otp = await bcrypt.hash(String(otpCode), 10);\n    let signupData = { ...data, password, otp, role: \"user\" };\n    let result = await addItem(signupData, \"pending_users\");\n    await sendEmail({\n        to: signupData.email,\n        subject: \"otp code\",\n        text: `Your otp is ${otpCode}`,\n    });\n    return { success: true, id: result.rows[0].id, message: \"Message is sent\" };\n}\n"
  },
  {
    "path": "src/services/auth/verifyEmailService.js",
    "content": "import { addItem, findItem } from \"#repositories/databaseRepository.js\";\nimport verifyEmailSchema from \"#schemas/verifyEmailSchema.js\";\nimport AppError from \"#utils/appError.js\";\nimport bcrypt from \"bcrypt\";\nexport default async function verifyEmailService(body) {\n    let verify = verifyEmailSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = verify.data;\n    let user = (await findItem({ id: data.id }, \"pending_users\")).rows[0];\n    if (!user) throw new AppError(\"ID was not found\", 404);\n    if (300000 < Date.now() - new Date(user.created_at + \"Z\").getTime())\n        throw new AppError(\"Expired OTP\", 401);\n    let match = await bcrypt.compare(data.otp, user.otp);\n    if (!match) throw new AppError(\"Invalid OTP\", 401);\n    let foundUsers = (\n        await findItem(\n            { username: user.username, email: user.email },\n            \"users\",\n            1,\n            false,\n        )\n    ).rows[0];\n    if (foundUsers) {\n        let err = foundUsers.username == data.username ? \"Username\" : \"Email\";\n        throw new AppError(err + \" is already used\", 409);\n    }\n\n    delete user.otp;\n    delete user.created_at;\n    await addItem(user, \"users\");\n    return { success: true, message: \"Email verified\" };\n}\n"
  },
  {
    "path": "src/services/cart/getCartService.js",
    "content": "import { findItem } from \"#repositories/databaseRepository.js\";\n\nexport default async function getCartService(user) {\n    console.log(user)\n    let result = await findItem({ user_id: user.sub }, \"carts\");\n    return {\n        cart: result.cart,\n    };\n}\n"
  },
  {
    "path": "src/services/cart/setCartService.js",
    "content": "import {\n    updateItem,\n    findItem,\n    setCart,\n} from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\nimport cartSchema from \"#schemas/cartSchema.js\";\n\nexport default async function setCartService(items, user) {\n    let verify = cartSchema.safeParse(items);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = { items: verify.data.cart, user_id: user.sub };\n    // let badCart = [];\n    // for (let i = 0; i < data.length; i++) {\n    //     let item = await findItem({ _id: data[i] }, \"products\");\n    //     if (!item) badCart.push(item);\n    // }\n    // if (badCart.length > 0)\n    //     throw new AppError(`${badCart} Does not Exist`, 400);\n    let result = await setCart(data);\n    return {\n        id: result,\n        message: \"Product is added\",\n    };\n}\n"
  },
  {
    "path": "src/services/emailService.js",
    "content": "import nodemailer from \"nodemailer\";\nimport { configDotenv } from \"dotenv\";\nconfigDotenv();\nconst transporter = nodemailer.createTransport({\n    host: process.env.EMAIL_HOST,\n    port: 587,\n    auth: {\n        user: process.env.EMAIL_USER,\n        pass: process.env.EMAIL_PASSWORD,\n    },\n});\n\nexport default async function sendEmail(mail) {\n    const info = await transporter.sendMail({\n        from: process.env.EMAIL_USER,\n        to: mail.to,\n        subject: mail.subject,\n        text: mail.text,\n    });\n    // console.log(\"Message sent:\", info.messageId);\n    console.log(\"Preview URL:\", nodemailer.getTestMessageUrl(info));\n    return {success:true, message: \"Message is sent\"};\n}\n\n// sendEmail({ to: \"test1@user.com\", subject: \"test subject\", text: \"text ENV\" });\n"
  },
  {
    "path": "src/services/order/addOrderService.js",
    "content": "import { findItem, setOrder } from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\n\nexport default async function setOrderService(items, user) {\n    let order = (await findItem({ user_id: user.sub }, \"carts\")).rows;\n    let data = {\n        items: order,\n        user_id: user.sub,\n        address: user.role,\n    };\n    let result = await setOrder(data);\n    return {\n        id: result,\n        message: \"Order is added\",\n    };\n}\n"
  },
  {
    "path": "src/services/order/getOrderService.js",
    "content": "import { findItem } from \"#repositories/databaseRepository.js\";\n\nexport default async function getCartService(user) {\n    let result = await findItem({ user_id: user.sub }, \"orders\");\n    return {\n        orders: result.rows,\n    };\n}\n"
  },
  {
    "path": "src/services/product/addProductService.js",
    "content": "import { addItem } from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\nimport productSchema from \"#schemas/productSchema.js\";\n\nexport default async function addProductService(body) {\n    let verify = productSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = verify.data;\n    let result = (await addItem(data, 'products')).rows[0].id;\n    return {\n        id: result,\n        message: \"Product is added\",\n    };\n}\n"
  },
  {
    "path": "src/services/product/deleteProductService.js",
    "content": "import { deleteItem } from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\nimport idSchema from \"#schemas/idSchema.js\";\n\nexport default async function removeProductService(body) {\n    let verify = idSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = { id: verify.data.id };\n    let result = await deleteItem(data, \"products\");\n    return {\n        deletedCount: result.rowCount,\n    };\n}\n"
  },
  {
    "path": "src/services/product/getProductsService.js",
    "content": "import { findItem } from \"#repositories/databaseRepository.js\";\n\nexport default async function getProductsService() {\n    let result = await findItem({}, \"products\");\n    return {\n        items: result,\n    };\n}\n"
  },
  {
    "path": "src/services/product/updateProductService.js",
    "content": "import { updateItem } from \"#repositories/databaseRepository.js\";\nimport AppError from \"#utils/appError.js\";\nimport productUpdateSchema from \"#schemas/productUpdateSchema.js\";\n\nexport default async function updateProductService(body) {\n    let verify = productUpdateSchema.safeParse(body);\n    if (!verify.success)\n        throw new AppError(\"Invalid \" + verify.error.issues[0].path[0], 400);\n    let data = verify.data;\n    let id = data.id;\n    delete data.id;\n    let result = await updateItem(data, { id }, \"products\");\n    return {\n        matchedCount: result.rowCount,\n    };\n}\n"
  },
  {
    "path": "src/utils/appError.js",
    "content": "export default class AppError extends Error {\n    constructor(message, status) {\n        super(message);\n        this.status = status;\n        this.isOperational = true;\n    }\n}\n"
  },
  {
    "path": "src/utils/errorsHandle.js",
    "content": "export default function errorsHandle(err, req, res, next) {\n    console.log(\"This error handler\");\n    res.status(err.status || 500).json({\n        success: false,\n        message: err.message || \"Internal Server Error\",\n    });\n}\n"
  },
  {
    "path": "src/utils/generateOTP.js",
    "content": "import crypto from \"crypto\"\n\nexport default function generateOTP()\n{\n    return crypto.randomInt(1000000, 9999999)\n}\n\n"
  },
  {
    "path": "src/utils/generateToken.js",
    "content": "import jwt from \"jsonwebtoken\";\nimport crypto from \"crypto\";\nimport { configDotenv } from \"dotenv\";\nimport { addItem } from \"#repositories/databaseRepository.js\";\n\nconfigDotenv();\n\nexport default async function generateToken(user, type, expire) {\n    let jti = crypto.randomUUID();\n    let token = jwt.sign(\n        { ...user, jti, type, createdAt: new Date() },\n        process.env[type.toUpperCase() + \"_JWT_KEY\"],\n        {\n            expiresIn: expire,\n        },\n    );\n\n    if (type == \"refresh\") await addItem({ jti: jti }, \"tokens\");\n    return token;\n}\n\n// console.log(await generateToken({ user: \"demo\" },'access', \"7d\"));\n"
  },
  {
    "path": "src/utils/rateLimit.js",
    "content": "import rateLimit from \"express-rate-limit\";\n\nexport default function setLimit(limit, time) {\n    return rateLimit({\n        windowMs: time * 60 * 1000,\n        limit,\n        message: {error: \"Too many requests\"},\n        standardHeaders: true,\n        legacyHeaders: true,\n    });\n}\n"
  },
  {
    "path": "src/utils/storeRefreshToken.js",
    "content": "export default function storeRefreshToken(token, res) {\n    res.cookie(\"refreshToken\", token, {\n        httpOnly: true,\n        secure: false,\n        sameSite: \"strict\",\n        path: \"/auth/token/refresh\",\n    });\n}\n"
  }
]