From 858b6cf3e431e29e135b7283fd3796fcdd3ff914 Mon Sep 17 00:00:00 2001 From: vzaisun Date: Thu, 15 Feb 2024 15:43:29 +0530 Subject: [PATCH 01/15] added stripe payment --- .env | 4 ++- app.js | 3 ++ package-lock.json | 27 +++++++++++++++++ package.json | 1 + .../ShippingAddresses/ShippingAddressModel.js | 2 +- resources/StripePayment/stripeController.js | 12 ++++++++ resources/StripePayment/stripeModel.js | 29 +++++++++++++++++++ resources/StripePayment/stripeRoute.js | 8 +++++ 8 files changed, 84 insertions(+), 2 deletions(-) create mode 100644 resources/StripePayment/stripeController.js create mode 100644 resources/StripePayment/stripeModel.js create mode 100644 resources/StripePayment/stripeRoute.js diff --git a/.env b/.env index b3dc341..0b34646 100644 --- a/.env +++ b/.env @@ -34,4 +34,6 @@ SMPT_PASSWORD="ND5sgVnWtrpUFfTb" # SMPT_PASSWORD="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" PAYPAL_CLIENT_ID="AemCjVuWswklp1sWUo4peCFg9eS4bofMsMR0RCrVRB2DifYR1IUSrWqtHpVmQlrVMKTI2cWZXLJAdYwn" -PAYPAL_CLIENT_SECRET="EAo0Y9ff3jpHHg1QAbftdebfh7cb_-vnebhQrP9KALbCVer908yx2tO2eHO39r7EJSfqc4D69Qgx8R31" \ No newline at end of file +PAYPAL_CLIENT_SECRET="EAo0Y9ff3jpHHg1QAbftdebfh7cb_-vnebhQrP9KALbCVer908yx2tO2eHO39r7EJSfqc4D69Qgx8R31" + +STRIPE_SECRET="sk_test_51OhPRdSG6gbAOwcEid1GavJ4FTD0ZuHVTferdvJwKal77RlMtFJGBzL5GjtL0ie8ZJztsGjUWi8DWrnw1pDdDRGS005Hk0ahql" \ No newline at end of file diff --git a/app.js b/app.js index 3ce08dc..dd94073 100644 --- a/app.js +++ b/app.js @@ -67,6 +67,7 @@ import TaxRouter from "./resources/Tax/tax_routes.js"; //specialties import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js"; import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js"; +import stripeRoute from "./resources/StripePayment/stripeRoute.js"; //short urls // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; @@ -112,6 +113,8 @@ app.use("/api/business", orderRoute); app.use("/api/tax", TaxRouter); //config app.use("/api/config", ConfigRouter); + +app.use("/api/stripe",stripeRoute); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/package-lock.json b/package-lock.json index 1afcf6f..f83d64d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "nodemailer": "^6.9.4", "nodemon": "^3.0.1", "secure-random-password": "^0.2.3", + "stripe": "^14.16.0", "uuid": "^9.0.1", "validator": "^13.7.0" } @@ -2209,6 +2210,32 @@ "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "optional": true }, + "node_modules/stripe": { + "version": "14.16.0", + "resolved": "https://registry.npmjs.org/stripe/-/stripe-14.16.0.tgz", + "integrity": "sha512-1gOr2LzafWV84cPIO5Md/QPh4XVPLKULVuRpBVOV3Plq3seiHmg/eeOktX+hDl8jpNZuORHYaUJGrNqrABLwdg==", + "dependencies": { + "@types/node": ">=8.1.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=12.*" + } + }, + "node_modules/stripe/node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 9379766..15b643d 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "nodemailer": "^6.9.4", "nodemon": "^3.0.1", "secure-random-password": "^0.2.3", + "stripe": "^14.16.0", "uuid": "^9.0.1", "validator": "^13.7.0" } diff --git a/resources/ShippingAddresses/ShippingAddressModel.js b/resources/ShippingAddresses/ShippingAddressModel.js index d2f4f5a..d8eacb0 100644 --- a/resources/ShippingAddresses/ShippingAddressModel.js +++ b/resources/ShippingAddresses/ShippingAddressModel.js @@ -32,7 +32,7 @@ const shippingAddressSchema = new mongoose.Schema( trim: true, // Add a regular expression to enforce a specific postal code format // For example, assuming a 5-digit format for the United States - match: /^\d{5}$/, + match: /^\d{6}$/, }, country: { type: String, diff --git a/resources/StripePayment/stripeController.js b/resources/StripePayment/stripeController.js new file mode 100644 index 0000000..21d3346 --- /dev/null +++ b/resources/StripePayment/stripeController.js @@ -0,0 +1,12 @@ +import { createCheckoutSession } from './stripeModel.js'; + +export async function createCheckoutSessionController(req, res) { + try { + const body = req.body; + const sessionId = await createCheckoutSession(body); + res.json({ id: sessionId }); + } catch (error) { + console.error("Error creating checkout session:", error); + res.status(500).json({ error: "Failed to create checkout session" }); + } +} diff --git a/resources/StripePayment/stripeModel.js b/resources/StripePayment/stripeModel.js new file mode 100644 index 0000000..bd514d0 --- /dev/null +++ b/resources/StripePayment/stripeModel.js @@ -0,0 +1,29 @@ +import Stripe from 'stripe'; + +const stripe = new Stripe(process.env.STRIPE_SECRET); + +export async function createCheckoutSession(body) { + const lineItems = body.products.map(({ product, quantity }) => ({ + price_data: { + currency: "usd", + product_data: { + name: product.name, + images: [product.image[0].url] // assuming you want to use the first image URL + }, + unit_amount: Math.round(product.price * 100), // Ensure proper conversion to cents + }, + quantity: quantity + })); + + + + const session = await stripe.checkout.sessions.create({ + payment_method_types: ["card"], + line_items: lineItems, + mode: "payment", + success_url: "http://localhost:5173/order-complete", // Provide your success URL here + cancel_url: "http://localhost:5173/cart" // Provide your cancel URL here + }); + + return session.id; +} diff --git a/resources/StripePayment/stripeRoute.js b/resources/StripePayment/stripeRoute.js new file mode 100644 index 0000000..c22bae6 --- /dev/null +++ b/resources/StripePayment/stripeRoute.js @@ -0,0 +1,8 @@ +import express from "express"; +import { createCheckoutSessionController } from "./stripeController.js"; + +const router = express.Router(); + +router.post("/create-checkout-session", createCheckoutSessionController); + +export default router; From 468238a28f227c8a90165933a1e12bc97b1e982b Mon Sep 17 00:00:00 2001 From: vzaisun Date: Tue, 5 Mar 2024 15:12:11 +0530 Subject: [PATCH 02/15] seo api added --- app.js | 4 ++ .../ContactRequests/ContactRequestsModel.js | 3 -- resources/SEO&Analytics/SEOController.js | 39 +++++++++++++++++++ resources/SEO&Analytics/SEOModel.js | 33 ++++++++++++++++ resources/SEO&Analytics/SEORouter.js | 12 ++++++ 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 resources/SEO&Analytics/SEOController.js create mode 100644 resources/SEO&Analytics/SEOModel.js create mode 100644 resources/SEO&Analytics/SEORouter.js diff --git a/app.js b/app.js index 705bafb..c67ffcc 100644 --- a/app.js +++ b/app.js @@ -157,6 +157,8 @@ import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js"; import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js"; import stripeRoute from "./resources/StripePayment/stripeRoute.js"; +import SeoRoute from "./resources/SEO&Analytics/SEORouter.js" + //short urls // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; @@ -203,6 +205,8 @@ app.use("/api/tax", TaxRouter); app.use("/api/config", ConfigRouter); app.use("/api/stripe",stripeRoute); + +app.use("/api/seo",SeoRoute); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/resources/ContactRequests/ContactRequestsModel.js b/resources/ContactRequests/ContactRequestsModel.js index ccad01d..6dcca01 100644 --- a/resources/ContactRequests/ContactRequestsModel.js +++ b/resources/ContactRequests/ContactRequestsModel.js @@ -1,7 +1,4 @@ - - - import mongoose from "mongoose"; const { Schema, model } = mongoose; diff --git a/resources/SEO&Analytics/SEOController.js b/resources/SEO&Analytics/SEOController.js new file mode 100644 index 0000000..8a25cfe --- /dev/null +++ b/resources/SEO&Analytics/SEOController.js @@ -0,0 +1,39 @@ +import {SeoRequest} from "./SEOModel.js"; + +export const AddNewSeoRequest = async (req, res) => { + try { + + let existingSeoRequest = await SeoRequest.findOne(); + + if (existingSeoRequest) { + + existingSeoRequest.GoogleTag = req.body.GoogleTag; + existingSeoRequest.FacebookPixel = req.body.FacebookPixel; + existingSeoRequest.GoogleAnalytics = req.body.GoogleAnalytics; + existingSeoRequest.MicrosoftClarity=req.body.MicrosoftClarity; + + + existingSeoRequest = await existingSeoRequest.save(); + + res.status(200).json({ + success: true, + seorequest: existingSeoRequest, + message: "Seo Request Updated", + }); + } else { + + const newSeoRequest = await SeoRequest.create(req.body); + + res.status(201).json({ + success: true, + seorequest: newSeoRequest, + message: "Seo Request Added", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; diff --git a/resources/SEO&Analytics/SEOModel.js b/resources/SEO&Analytics/SEOModel.js new file mode 100644 index 0000000..625cb84 --- /dev/null +++ b/resources/SEO&Analytics/SEOModel.js @@ -0,0 +1,33 @@ +import mongoose from "mongoose"; + +const { Schema, model } = mongoose; + +const SeoRequestSchema = new mongoose.Schema( + { + + GoogleTag: { + type: String, + maxLength: [25, "tag cannot exceed 25 characters"], + required: [true, "Please Enter google tag "], + }, + FacebookPixel: { + type: String, + maxLength: [25, "tag cannot exceed 25 characters"], + required: [true, "Please Enter Facebook Pixel "], + }, + GoogleAnalytics: { + type: String, + maxLength: [500, "google analytics cannot exceed 500 characters"], + required: [true, "Please Enter google analytics"], + }, + MicrosoftClarity: { + type: String, + maxLength: [500, "Microsoft clarity cannot exceed 500 characters"], + required: [true, "Please Enter microsoft clarity"], + }, + + }, + { timestamps: true, versionKey: false } +); + +export const SeoRequest = mongoose.model("SeoRequest", SeoRequestSchema); diff --git a/resources/SEO&Analytics/SEORouter.js b/resources/SEO&Analytics/SEORouter.js new file mode 100644 index 0000000..33bf606 --- /dev/null +++ b/resources/SEO&Analytics/SEORouter.js @@ -0,0 +1,12 @@ +import express from "express"; +import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; +import { AddNewSeoRequest } from "./SEOController.js"; + +const router = express.Router(); + +router + .route("/new") + .post(isAuthenticatedUser, authorizeRoles("admin"), AddNewSeoRequest); + + +export default router; From f85a516808f7914da76b205a6c244f9f35b36b71 Mon Sep 17 00:00:00 2001 From: Raj-varu Date: Tue, 12 Mar 2024 13:53:24 +0530 Subject: [PATCH 03/15] Affiliate API and Coupon API --- app.js | 16 +- .../Affiliate/AffiliateController.js | 291 +++++++++++++ .../Affiliate/AffiliateModel.js | 122 ++++++ .../Affiliate/AffiliateRoute.js | 69 ++++ .../Coupon/CouponController.js | 383 ++++++++++++++++++ .../Affiliate&Coupon/Coupon/CouponRoute.js | 76 ++++ 6 files changed, 954 insertions(+), 3 deletions(-) create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateController.js create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateModel.js create mode 100644 resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js create mode 100644 resources/Affiliate&Coupon/Coupon/CouponController.js create mode 100644 resources/Affiliate&Coupon/Coupon/CouponRoute.js diff --git a/app.js b/app.js index c67ffcc..e10bf7d 100644 --- a/app.js +++ b/app.js @@ -157,8 +157,12 @@ import SpecialtiesRouter from "./resources/Specialties/SpecialtiesRoute.js"; import ShippingAddressRoute from "./resources/ShippingAddresses/ShippingAddressRoute.js"; import stripeRoute from "./resources/StripePayment/stripeRoute.js"; -import SeoRoute from "./resources/SEO&Analytics/SEORouter.js" +import SeoRoute from "./resources/SEO&Analytics/SEORouter.js"; +//Affiliate Routes +import AffiliateRoute from "./resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js"; +//Coupon Routes +import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; //short urls // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; @@ -204,9 +208,15 @@ app.use("/api/tax", TaxRouter); //config app.use("/api/config", ConfigRouter); -app.use("/api/stripe",stripeRoute); +app.use("/api/stripe", stripeRoute); -app.use("/api/seo",SeoRoute); +app.use("/api/seo", SeoRoute); + +//Affiliates +app.use("/api/v1/affiliate", AffiliateRoute); + +//Coupons +app.use("/api/v1/coupon", CouponRoute); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateController.js b/resources/Affiliate&Coupon/Affiliate/AffiliateController.js new file mode 100644 index 0000000..790f917 --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateController.js @@ -0,0 +1,291 @@ +import { AffiliateModel } from "./AffiliateModel.js"; + +// -----------------------------AFFILIATE & COUPONS ARE HARDLY BINDED DATA-------------------------------------------------------- +//Create Affiliate +export const createAffiliate = async (req, res) => { + try { + const result = req.body; + const affiliate = new AffiliateModel(result); + const savedData = await affiliate.save(); + if (savedData) { + return res + .status(201) + .json({ success: true, message: "Affiliate Added" }); + } + } catch (error) { + res.status(500).json({ + success: false, + message: error.message + .split(":") + .splice(1) + .join(":") + .trim() + .split(":") + .splice(1) + .join(":") + .trim(), + }); + } +}; + +//EDIT +export const editAffiliate = async (req, res) => { + const updateFields = {}; + + const { + name, + mobile, + email, + country, + state, + city, + address, + pincode, + nameAsBank, + accountNo, + ifsc, + bankName, + branchName, + } = req.body; + + // Add only the fields that are present in the request body to the updateFields object + if (name) updateFields.name = name; + if (mobile) updateFields.mobile = mobile; + if (email) updateFields.email = email; + if (country) updateFields.country = country; + if (state) { + updateFields.state = state; + } else { + updateFields.state = ""; + } + if (city) { + updateFields.city = city; + } else { + updateFields.city = ""; + } + if (address) updateFields.address = address; + if (pincode) updateFields.pincode = pincode; + if (nameAsBank) updateFields.nameAsBank = nameAsBank; + if (accountNo) updateFields.accountNo = accountNo; + if (ifsc) updateFields.ifsc = ifsc; + if (bankName) updateFields.bankName = bankName; + if (branchName) updateFields.branchName = branchName; + try { + const saveData = await AffiliateModel.findByIdAndUpdate( + { _id: req.params.id }, + { $set: updateFields }, + { new: true } + ); + res.json({ + success: true, + message: "Affiliate Updated Succesfully", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in Updation", + }); + } +}; +//DELETE +export const deleteAffiliate = async (req, res) => {}; +//PAY AFFILIATE TODO +export const payAffiliate = async (req, res) => { + // console.log(req.body); + const { noOfCoupons, amountToPay, amount, transecId, date, time } = req.body; + if ( + !req.params.id || + !noOfCoupons || + !amountToPay || + !amount || + !transecId || + !date || + !time + ) { + return res.status(400).json({ + success: false, + message: "Error in Payment", + }); + } + try { + const affiliate = await AffiliateModel.findById(req.params.id); + //Checking if it's valid data from the client + if ( + amountToPay != affiliate.total_earning - affiliate.paid_amount || + noOfCoupons != affiliate.coupon_claimed - affiliate.no_of_paid_coupon + ) { + return res.status(400).json({ + success: false, + message: "Data invalid", + }); + } + + // Construct the update operation + const updateOperation = { + $push: { + affiliate_pay_history: { + amount: amountToPay, + transecId: transecId, + date: date, + time: time, + }, + }, + $inc: { + paid_amount: amountToPay, + no_of_paid_coupon: noOfCoupons, + }, + }; + + // Execute the update operation + const updatedAffiliate = await AffiliateModel.findByIdAndUpdate( + req.params.id, + updateOperation, + { new: true } + ); + + return res.json({ + success: true, + message: "Payment Done Successfully", + updatedAffiliate: { updatedAffiliate }, + }); + } catch (error) { + console.error(error); + return res.status(400).json({ + success: false, + message: "Error in Payment", + }); + } +}; + +//GET ONE AFFLILIATE +export const getOneAffiliate = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + const resObj = { + name: saveData.name, + mobile: saveData.mobile, + email: saveData.email, + country: saveData.country, + state: saveData.state, + city: saveData.city, + address: saveData.address, + pincode: saveData.pincode, + nameAsBank: saveData.nameAsBank, + accountNo: saveData.accountNo, + ifsc: saveData.ifsc, + bankName: saveData.bankName, + branchName: saveData.branchName, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Affiliates", + }); + } + } +}; +//LIST ALL AFFILIATE +export const listAllAffiliate = async (req, res) => { + try { + const affiliate = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + coupon_claimed: 1, + coupon_code: 1, + total_earning: 1, + paid_amount: 1, + is_affiliate_active: 1, + } + ).sort({ createdAt: -1 }); + res.status(200).json({ + success: true, + message: affiliate, + }); + } catch (error) { + res.status(500).json({ + success: false, + messgae: error.message ? error.message : "Something went wrong!", + }); + } +}; + +//Activate & Deactivate Affiliates +export const suspendAffiliate = async (req, res) => { + const { id, is_affiliate_active } = req.body; + try { + const saveData = await AffiliateModel.findByIdAndUpdate(id, { + is_affiliate_active: is_affiliate_active, + }); + res.status(200).json({ + success: true, + message: "Success", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Affiliate Doesn't Exists", + }); + } +}; + +//Get Affiliate data for payment +export const getOneAffiliateForPay = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + const resObj = { + name: saveData.name, + coupon_claimed: saveData.coupon_claimed, + total_earning: saveData.total_earning, + paid_amount: saveData.paid_amount, + no_of_paid_coupon: saveData.no_of_paid_coupon, + affiliate_discount_amount: saveData.affiliate_discount_amount, + coupon_code: saveData.coupon_code, + nameAsBank: saveData.nameAsBank, + accountNo: saveData.accountNo, + ifsc: saveData.ifsc, + bankName: saveData.bankName, + branchName: saveData.branchName, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Affiliates", + }); + } + } +}; +//Get Affiliate data for History +export const affiliatePayHistory = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id).sort({ + updatedAt: -1, + }); + const resObj = { + affiliate_pay_history: saveData.affiliate_pay_history, + name: saveData.name, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting History", + }); + } + } +}; diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js b/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js new file mode 100644 index 0000000..928243e --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateModel.js @@ -0,0 +1,122 @@ +import mongoose from "mongoose"; +const { Schema, model } = mongoose; + +const couponUsedSchema = new Schema({ + userId: { type: String, required: true }, + orderId: { type: String, required: true }, + couponCode: { type: String, required: true }, + date: { type: String, required: true }, +}); +const affilitePaySchema = new Schema({ + amount: { type: Number, required: true }, + transecId: { type: String, required: true }, + date: { type: String, required: true }, + time: { type: String, required: true }, +}); +const affiliateSchema = new Schema( + { + name: { + type: String, + maxLength: [25, "name cannot exceed 25 characters"], + required: [true, "Please Enter Name"], + }, + mobile: { + type: Number, + maxLength: [10, "Mobile cannot exceed 10 characters"], + minlength: [10, "Invalid Mobile Number"], + required: [true, "Please Enter Mobile Number"], + unique: true, + }, + email: { + type: String, + required: [true, "Please Enter Email"], + unique: true, + }, + country: { + type: String, + required: [true, "Please Enter Country"], + }, + state: { + type: String, + }, + city: { + type: String, + }, + address: { + type: String, + required: [true, "Please Enter Address"], + }, + pincode: { + type: Number, + required: [true, "Please Enter Pincode"], + }, + nameAsBank: { + type: String, + required: [true, "Please Enter Name as Bank"], + }, + accountNo: { + type: Number, + required: [true, "Please Enter Account Number"], + unique: true, + }, + ifsc: { + type: String, + required: [true, "Please Enter IFSC code"], + }, + bankName: { + type: String, + required: [true, "Please Enter Bank Name"], + }, + branchName: { + type: String, + required: [true, "Please Enter Branch Name"], + }, + coupon_code: { + type: String, + unique: [true, "Coupon Alerady Exists"], + sparse: true, + }, + discount_amount: { + type: Number, + }, + affiliate_discount_amount: { + type: Number, + }, + valid_till: { + type: String, + }, + createdAt: { + type: String, + }, + + coupon_claimed: { + type: Number, + default: 0, + }, + total_earning: { + type: Number, + default: 0, + }, + paid_amount: { + type: Number, + default: 0, + }, + no_of_paid_coupon: { + type: Number, + default: 0, + }, + is_affiliate_active: { + type: Boolean, + default: true, + }, + is_coupon_active: { + type: Boolean, + default: false, + }, + coupon_used_history: [couponUsedSchema], + affiliate_pay_history: [affilitePaySchema], + }, + { timestamps: true } +); + +export const AffiliateModel = model("Affiliate", affiliateSchema); diff --git a/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js b/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js new file mode 100644 index 0000000..ccf9b66 --- /dev/null +++ b/resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js @@ -0,0 +1,69 @@ +import express from "express"; +import { + affiliatePayHistory, + createAffiliate, + editAffiliate, + getOneAffiliate, + getOneAffiliateForPay, + listAllAffiliate, + payAffiliate, + suspendAffiliate, +} from "./AffiliateController.js"; + +import { + isAuthenticatedUser, + authorizeRoles, +} from "../../../middlewares/auth.js"; + +const router = express.Router(); + +router.post( + "/create", + isAuthenticatedUser, + authorizeRoles("admin"), + createAffiliate +); +router.get( + "/getall", + isAuthenticatedUser, + authorizeRoles("admin"), + listAllAffiliate +); +router.get( + "/getone/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneAffiliate +); +router.patch( + "/edit/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + editAffiliate +); +router.patch( + "/suspend", + isAuthenticatedUser, + authorizeRoles("admin"), + suspendAffiliate +); +router.post( + "/pay/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + payAffiliate +); +router.get( + "/getpay/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneAffiliateForPay +); +router.get( + "/history/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + affiliatePayHistory +); + +export default router; diff --git a/resources/Affiliate&Coupon/Coupon/CouponController.js b/resources/Affiliate&Coupon/Coupon/CouponController.js new file mode 100644 index 0000000..b328f01 --- /dev/null +++ b/resources/Affiliate&Coupon/Coupon/CouponController.js @@ -0,0 +1,383 @@ +import { AffiliateModel } from "../Affiliate/AffiliateModel.js"; //Note AffiliteModel is binded with coupons + +//GET ALL Coupons +export const listAllCoupon = async (req, res) => { + try { + const coupon = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + coupon_code: 1, + discount_amount: 1, + affiliate_discount_amount: 1, + is_coupon_active: 1, + } + ).sort({ createdAt: -1 }); + const filteredCoupons = coupon.filter( + (data) => !(data.coupon_code == null) + ); + // console.log(filteredCoupons); + // console.log(coupon); + + res.status(200).json({ + success: true, + message: filteredCoupons, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went wrong!", + }); + } +}; +//CREATE Coupon (AKA Need to update Affiliate ) +export const createCoupon = async (req, res) => { + //creation of date + const date = new Date(); + const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const monthsOfYear = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ]; + const dayOfWeek = daysOfWeek[date.getUTCDay()]; + const dateOfMonth = date.getUTCDate(); + const month = monthsOfYear[date.getUTCMonth()]; + const year = date.getUTCFullYear(); + const formattedDate = `${dayOfWeek} ${dateOfMonth}-${month}-${year}`; + const { + coupon_code, + discount_amount, + valid_till, + is_coupon_active, + affiliate_discount_amount, + } = req.body; + try { + const { id } = req.body; + const update = { + coupon_code, + discount_amount, + valid_till, + affiliate_discount_amount, + createdAt: formattedDate, + is_coupon_active, + }; + const options = { new: true }; + const saveData = await AffiliateModel.findByIdAndUpdate( + id, + update, + options + ); + + if (saveData) { + res.json("done"); + } else { + res.status(404).json("Affiliate not found"); + } + } catch (error) { + res.status(400).json({ + success: true, + message: "Coupon Already Exists", + }); + console.log(error); + } +}; + +//GET AFFILIATE FOR COUPON LIST +export const listAffiliateCoupon = async (req, res) => { + try { + let resArr = []; + const coupon = await AffiliateModel.find( + {}, + { + name: 1, + _id: 1, + is_coupon_active: 1, + mobile: 1, + } + ); + for (let i = 0; i < coupon.length; i++) { + if (coupon[i].is_coupon_active == false) { + resArr.push(coupon[i]); + } + } + // console.log(resArr); + res.status(200).json({ + success: true, + message: resArr, + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went wrong!", + }); + } +}; +//EDIT COUPON +export const editCoupon = async (req, res) => { + const { + coupon_code, + discount_amount, + valid_till, + affiliate_discount_amount, + } = req.body; + + const updateFields = {}; + + // Add only the fields that are present in the request body to the updateFields object + if (coupon_code) updateFields.coupon_code = coupon_code; + if (discount_amount) updateFields.discount_amount = discount_amount; + if (valid_till) updateFields.valid_till = valid_till; + if (affiliate_discount_amount) + updateFields.affiliate_discount_amount = affiliate_discount_amount; + + try { + const saveData = await AffiliateModel.findByIdAndUpdate( + { _id: req.params.id }, + { $set: updateFields }, + { new: true } + ); + res.json({ + success: true, + message: "Coupon Edited Succesfully", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: error.message + ? error.message + .split(":") + .splice(1) + .join(":") + .trim() + .split(":") + .splice(1) + .join(":") + .trim() + : "Error in Editing Coupon", + }); + } +}; +//SUSPEND COUPON +export const suspendCoupon = async (req, res) => { + const { id, is_coupon_active } = req.body; + try { + const saveData = await AffiliateModel.findByIdAndUpdate(id, { + is_coupon_active: is_coupon_active, + }); + res.status(200).json({ + success: true, + message: "Success", + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Coupon Doesn't Exists", + }); + } +}; + +//GET ONE COUPON +export const getOneCoupon = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id); + + const resObj = { + name: saveData.name, + mobile: saveData.mobile, + coupon_code: saveData.coupon_code, + discount_amount: saveData.discount_amount, + valid_till: saveData.valid_till, + affiliate_discount_amount: saveData.affiliate_discount_amount, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting Coupons", + }); + } + } +}; + +//Validate Coupon----------------------- +export const validateCoupon = async (req, res) => { + const { coupon } = req.params; + + if (!coupon) { + return res.status(400).json({ + success: false, + message: "Coupon code is required", + }); + } + + try { + // Find the coupon data in the database + const couponData = await AffiliateModel.findOne({ coupon_code: coupon }); + + if (!couponData) { + return res.status(404).json({ + success: false, + message: "Coupon not found", + }); + } + + const { valid_till, discount_amount, is_coupon_active } = couponData; + //Check whether Coupon is Active or not + if (!is_coupon_active) { + return res.status(404).json({ + success: false, + message: "Coupon Code Expired", + }); + } + // Check if the coupon is expired + const currentDate = new Date(); + const expirationDate = new Date(valid_till); + + if (currentDate > expirationDate) { + return res.status(400).json({ + success: false, + message: "Coupon has expired", + }); + } + + // If coupon is valid, return success response + res.status(200).json({ + success: true, + message: "Coupon is valid", + discount_amount: discount_amount, + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; + +//PAY & HISTORY--------------------- +export const usedCoupon = async (req, res) => { + // Retrieve orderId and coupon_code from request body or query parameters + const { orderId, coupon_code, userId } = req.body; + if (!orderId || !coupon_code || !userId) { + return res.status(400).json({ + success: false, + message: "Error in getting OrderId or Coupon", + }); + } + + // Validating Coupon + try { + const couponData = await AffiliateModel.findOne({ + coupon_code: coupon_code, + }); + + // Check if the coupon exists + if (!couponData) { + return res.status(404).json({ + success: false, + message: "Coupon not found", + }); + } + + const { + valid_till, + is_coupon_active, + total_earning, + is_affiliate_active, + coupon_claimed, + affiliate_discount_amount, + _id, + } = couponData; + // console.log(couponData); + if (!is_coupon_active || !is_affiliate_active) { + return res.status(404).json({ + success: false, + message: "Coupon Code Expired", + }); + } + const currentDate = new Date(); + const expirationDate = new Date(valid_till); + + if (currentDate > expirationDate) { + return res.status(400).json({ + success: false, + message: "Coupon has expired", + }); + } + + AffiliateModel.findByIdAndUpdate( + _id, + { + $inc: { total_earning: affiliate_discount_amount, coupon_claimed: 1 }, + $push: { + coupon_used_history: { + orderId: orderId, + userId: userId, + date: currentDate, + couponCode: coupon_code, + }, + }, + }, + { new: true } + ) + .then(() => { + res.status(200).json({ + success: true, + message: "Coupon add success", + }); + }) + .catch((error) => { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + }); + } catch (error) { + console.error(error); + res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; +//Get Coupon data for History +export const couponPayHistory = async (req, res) => { + if (req.params?.id) { + try { + const saveData = await AffiliateModel.findById(req.params.id).sort({ + updatedAt: -1, + }); + // console.log(saveData.coupon_used_history); + const resObj = { + coupon_used_history: saveData.coupon_used_history, + coupon_code: saveData.coupon_code, + }; + res.status(200).json({ + success: true, + message: resObj, + }); + } catch (error) { + res.status(400).json({ + success: false, + message: "Error in getting History", + }); + } + } +}; diff --git a/resources/Affiliate&Coupon/Coupon/CouponRoute.js b/resources/Affiliate&Coupon/Coupon/CouponRoute.js new file mode 100644 index 0000000..bead939 --- /dev/null +++ b/resources/Affiliate&Coupon/Coupon/CouponRoute.js @@ -0,0 +1,76 @@ +import express from "express"; +import { + couponPayHistory, + createCoupon, + editCoupon, + getOneCoupon, + listAffiliateCoupon, + listAllCoupon, + suspendCoupon, + usedCoupon, + validateCoupon, +} from "./CouponController.js"; + +import { + isAuthenticatedUser, + authorizeRoles, +} from "../../../middlewares/auth.js"; + +const router = express.Router(); +router.get( + "/getall", + isAuthenticatedUser, + authorizeRoles("admin"), + listAllCoupon +); +router.patch( + "/create", + isAuthenticatedUser, + authorizeRoles("admin"), + createCoupon +); +router.get( + "/getaffiliate", + isAuthenticatedUser, + authorizeRoles("admin"), + listAffiliateCoupon +); +router.patch( + "/edit/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + editCoupon +); +router.get( + "/getone/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + getOneCoupon +); +router.get("/validcoupon/:coupon", validateCoupon); +router.patch( + "/suspend", + isAuthenticatedUser, + authorizeRoles("admin"), + suspendCoupon +); +router.patch( + "/paycoupon", + // isAuthenticatedUser, + usedCoupon +); +/* url:http://localhost:5000/api/v1/coupon/paycoupon + json structure to paycoupon , Need Header to be auth +{ + "userId":"random1", + "orderId":"12s213", + "coupon_code":"3000MONY" +}*/ +router.get( + "/history/:id", + isAuthenticatedUser, + authorizeRoles("admin"), + couponPayHistory +); + +export default router; From f4313fcef58ebcaac6f3ffc9ce3f394266c1788d Mon Sep 17 00:00:00 2001 From: Raj-varu Date: Tue, 12 Mar 2024 15:45:58 +0530 Subject: [PATCH 04/15] Unique OrderId Validation for Coupon Use --- .../Coupon/CouponController.js | 37 +++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/resources/Affiliate&Coupon/Coupon/CouponController.js b/resources/Affiliate&Coupon/Coupon/CouponController.js index b328f01..285d145 100644 --- a/resources/Affiliate&Coupon/Coupon/CouponController.js +++ b/resources/Affiliate&Coupon/Coupon/CouponController.js @@ -286,21 +286,52 @@ export const usedCoupon = async (req, res) => { const couponData = await AffiliateModel.findOne({ coupon_code: coupon_code, }); + //order exists or not - // Check if the coupon exists if (!couponData) { + // Check if the coupon exists return res.status(404).json({ success: false, message: "Coupon not found", }); } + // Check if orderId is unique + try { + const isOrderIdUnique = await AffiliateModel.find( + {}, + { + coupon_used_history: 1, + } + ); + let orderIdFound = false; + + isOrderIdUnique.forEach((data) => { + data.coupon_used_history.forEach((subItem) => { + if (subItem.orderId == orderId) { + orderIdFound = true; + } + }); + }); + + if (orderIdFound) { + return res.status(400).json({ + success: false, + message: "Error: OrderId already used", + }); + } + } catch (error) { + console.error(error); + return res.status(500).json({ + success: false, + message: "Internal server error", + }); + } + //If not unique then create const { valid_till, is_coupon_active, - total_earning, is_affiliate_active, - coupon_claimed, affiliate_discount_amount, _id, } = couponData; From 7c5cc17475628e06694c4d27635b2e31994229db Mon Sep 17 00:00:00 2001 From: roshangarg28 Date: Fri, 15 Mar 2024 10:43:21 +0530 Subject: [PATCH 05/15] webkook test --- resources/Orders/StripeCheckOutController.js | 18 ++++++--- resources/Orders/orderController.js | 27 ++++++++++--- resources/Orders/orderModel.js | 2 +- resources/Orders/orderRoute.js | 4 +- resources/StripePayment/stripeController.js | 17 ++++---- resources/StripePayment/stripeModel.js | 42 ++++++++++---------- 6 files changed, 65 insertions(+), 45 deletions(-) diff --git a/resources/Orders/StripeCheckOutController.js b/resources/Orders/StripeCheckOutController.js index b92eb18..81f6417 100644 --- a/resources/Orders/StripeCheckOutController.js +++ b/resources/Orders/StripeCheckOutController.js @@ -11,7 +11,7 @@ import { shippingAddress } from "../ShippingAddresses/ShippingAddressModel.js"; import sendEmail from "../../Utils/sendEmail.js"; // const endpointSecret = STRIPE_SECRET_KEY; //generate unique order id -const generateUniqueOrderId = async () => { +export const generateUniqueOrderId = async () => { const currentYear = new Date().getFullYear(); // Find the latest order to get the last serial number const latestOrder = await Order.findOne({}, {}, { sort: { orderID: -1 } }); @@ -36,6 +36,7 @@ export const handlePayment = async (req, res) => { if (!email) return res.status(400).send({ message: "Please enter the email" }); const { address, cart, subtotal } = req.body; + if (cart.length < 1) return res.status(400).json({ message: "cart is empty!" }); switch (true) { @@ -48,7 +49,7 @@ export const handlePayment = async (req, res) => { } } let addss = await shippingAddress.findById(address); - console.log(addss?.postalCode); + let shipping = { first_Name: addss.first_Name, last_Name: addss.last_Name, @@ -102,12 +103,19 @@ export const handlePayment = async (req, res) => { // Add any other key-value pairs as needed }, - success_url: `${process.env.FRONTEND_URL}/cart`, - cancel_url: `${process.env.FRONTEND_URL}/error`, + shipping_address_collection: { + allowed_countries: ["IN"], + // Allow only India for INR transactions + }, + billing_address_collection: "required", + success_url: "http://localhost:5173/order-complete", // Provide your success URL here + cancel_url: "http://localhost:5173/cart", }); // res.json({ sessionId: session.id }); - res.status(200).send({ message: "order created", url: session.url }); + res + .status(200) + .send({ message: "order created", url: session.url, id: session.id }); } } catch (err) { console.log(err); diff --git a/resources/Orders/orderController.js b/resources/Orders/orderController.js index cf73972..77dd538 100644 --- a/resources/Orders/orderController.js +++ b/resources/Orders/orderController.js @@ -1,3 +1,4 @@ +import sendEmail from "../../Utils/sendEmail.js"; import { Order } from "./orderModel.js"; export const getAllOrder = async (req, res) => { @@ -44,7 +45,7 @@ export const getSingleOrder = async (req, res) => { const order = await Order.findById(req.params.id) .populate({ path: "user", - select: "name -_id", + select: "name email -_id", }) .populate({ path: "shippingInfo.addressId", @@ -67,13 +68,13 @@ export const getSingleOrder = async (req, res) => { //get self User Order export const getUserSelf = async (req, res) => { + if (!req?.user) return res.status(400).json({ message: "please login !" }); try { const order = await Order.find({ - user: req.user._id, + user: req.user?._id, payment_status: "success", - }) - .populate("shippingInfo.addressId") - .sort({ createdAt: -1 }); + }).sort({ createdAt: -1 }); + if (order) { return res.status(200).json({ success: true, @@ -117,6 +118,7 @@ export const deleteOneOrder = async (req, res) => { export const updateOrderStatusById = async (req, res) => { try { let body = { orderStatus: req.body.status }; + const currentDate = new Date(); body["status_timeline." + req.body.status] = currentDate; // if (req.body?.package_weight) body.package_weight = req.body.package_weight; @@ -151,6 +153,21 @@ export const updateOrderStatusById = async (req, res) => { body["courier_name"] = req.body.courierName; body["courier_tracking_id"] = req.body.TrackingID; await Order.findByIdAndUpdate(order._id, body); + await sendEmail({ + to: `${req.body.sendemail}`, // Change to your recipient + + from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender + + subject: `Your Order is On Its Way!`, + html: `

Welcome to Smellika - Let the Shopping Begin!

+ Hi ${req.body?.customerName}, + +

Great news! Your order has been confirmed. Here are the details

+
+ Best regards,
+ + Team Smellika`, + }); return res .status(200) .json({ status: "ok", message: "Order status updated successfully!" }); diff --git a/resources/Orders/orderModel.js b/resources/Orders/orderModel.js index 470a46b..d24ce3a 100644 --- a/resources/Orders/orderModel.js +++ b/resources/Orders/orderModel.js @@ -117,7 +117,7 @@ const orderSchema = new mongoose.Schema( "cancelled", "returned", ], - // default: "new", + default: "new", }, // paypal_payer_id: { type: String }, diff --git a/resources/Orders/orderRoute.js b/resources/Orders/orderRoute.js index b4ced7a..d6a9780 100644 --- a/resources/Orders/orderRoute.js +++ b/resources/Orders/orderRoute.js @@ -28,9 +28,7 @@ router.route("/:orderID/capture/payment").post(captureOrderPayment); // ----------------------stripe checkOut-----------------// // app.post("/webhook", express.raw({ type: "application/json" }), webhook); -router - .route("/stripe-checkout-session") - .post(isAuthenticatedUser, handlePayment); +router.route("/stripe-checkout").post(isAuthenticatedUser, handlePayment); router .route("/webhook") .post(express.raw({ type: "application/json" }), webhook); diff --git a/resources/StripePayment/stripeController.js b/resources/StripePayment/stripeController.js index 21d3346..eba43e0 100644 --- a/resources/StripePayment/stripeController.js +++ b/resources/StripePayment/stripeController.js @@ -1,12 +1,11 @@ -import { createCheckoutSession } from './stripeModel.js'; +import { createCheckoutSession } from "./stripeModel.js"; export async function createCheckoutSessionController(req, res) { - try { - const body = req.body; - const sessionId = await createCheckoutSession(body); - res.json({ id: sessionId }); - } catch (error) { - console.error("Error creating checkout session:", error); - res.status(500).json({ error: "Failed to create checkout session" }); - } + try { + const sessionId = await createCheckoutSession(req.body); + res.json({ id: sessionId }); + } catch (error) { + console.error("Error creating checkout session:", error); + res.status(500).json({ error: "Failed to create checkout session" }); + } } diff --git a/resources/StripePayment/stripeModel.js b/resources/StripePayment/stripeModel.js index bd514d0..47bfc59 100644 --- a/resources/StripePayment/stripeModel.js +++ b/resources/StripePayment/stripeModel.js @@ -1,29 +1,27 @@ -import Stripe from 'stripe'; +import Stripe from "stripe"; const stripe = new Stripe(process.env.STRIPE_SECRET); export async function createCheckoutSession(body) { - const lineItems = body.products.map(({ product, quantity }) => ({ - price_data: { - currency: "usd", - product_data: { - name: product.name, - images: [product.image[0].url] // assuming you want to use the first image URL - }, - unit_amount: Math.round(product.price * 100), // Ensure proper conversion to cents - }, - quantity: quantity - })); - - + const lineItems = body.products.map(({ product, quantity }) => ({ + price_data: { + currency: "usd", + product_data: { + name: product.name, + images: [product.image[0].url], // assuming you want to use the first image URL + }, + unit_amount: Math.round(product.price * 100), // Ensure proper conversion to cents + }, + quantity: quantity, + })); - const session = await stripe.checkout.sessions.create({ - payment_method_types: ["card"], - line_items: lineItems, - mode: "payment", - success_url: "http://localhost:5173/order-complete", // Provide your success URL here - cancel_url: "http://localhost:5173/cart" // Provide your cancel URL here - }); + const session = await stripe.checkout.sessions.create({ + payment_method_types: ["card"], + line_items: lineItems, + mode: "payment", + success_url: "http://localhost:5173/order-complete", // Provide your success URL here + cancel_url: "http://localhost:5173/cart", // Provide your cancel URL here + }); - return session.id; + return session.id; } From 79d133e238d631c76f122ce44ca6bf76bfcf5633 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Wed, 20 Mar 2024 10:32:02 +0530 Subject: [PATCH 06/15] SupportTicket Api Updated --- .env | 1 + app.js | 6 +- resources/Supports/supportController.js | 292 ++++++++++++++++++++++++ resources/Supports/supportModel.js | 67 ++++++ resources/Supports/supportRoute.js | 32 +++ 5 files changed, 396 insertions(+), 2 deletions(-) create mode 100644 resources/Supports/supportController.js create mode 100644 resources/Supports/supportModel.js create mode 100644 resources/Supports/supportRoute.js diff --git a/.env b/.env index ed782b0..e09ecfa 100644 --- a/.env +++ b/.env @@ -1,6 +1,7 @@ DB_URL="mongodb+srv://smellica:Anjefef23dnsfjne@cluster0.c5gfqzm.mongodb.net/" +# DB_URL="mongodb://localhost:27017/smellica" PORT = 5000 JWT_SECRET = jdvnvjwrniwj4562ddsjn6@1xsbfeh@wre4Njdf; diff --git a/app.js b/app.js index e10bf7d..701ad0d 100644 --- a/app.js +++ b/app.js @@ -165,7 +165,8 @@ import AffiliateRoute from "./resources/Affiliate&Coupon/Affiliate/AffiliateRout import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; //short urls // import ShortUrlRouter from "./resources/Businesses/Short_Urls/ShortUrlRoute.js"; - +//support Ticket +import SupportRouter from "./resources/Supports/supportRoute.js"; app.use("/api/v1/", user); //Product @@ -225,5 +226,6 @@ app.use("/api/v1/coupon", CouponRoute); // app.use("/api/appointment", AppointmentRouter); //short urls // app.use("/api/shorturl", ShortUrlRouter); - +//Support +app.use("/api", SupportRouter); export default app; diff --git a/resources/Supports/supportController.js b/resources/Supports/supportController.js new file mode 100644 index 0000000..d25f406 --- /dev/null +++ b/resources/Supports/supportController.js @@ -0,0 +1,292 @@ +import { Support } from "./supportModel.js"; +import cloudinary from "../../Utils/cloudinary.js"; + +export const createSupport = async (req, res) => { + // console.log(req.body); + // console.log(req.files.image); + try { + // const { ticketId, createdOn, subject, description } = req.body; + if(req.files && req.files.image){ + let images = []; + let Allfiles = req.files.image; + if (typeof Allfiles.tempFilePath === "string") { + let filepath = Allfiles.tempFilePath; + + images.push(filepath); + } else { + Allfiles.map((item) => { + images.push(item.tempFilePath); + }); + } + + const imagesLinks = []; + for (let i = 0; i < images.length; i++) { + const result = await cloudinary.v2.uploader.upload(images[i], { + folder: "smellica/CustomerSupport", + }); + + imagesLinks.push({ + public_id: result.public_id, + url: result.secure_url, + }); + } + req.body.image = imagesLinks; + } + req.body.addedBy = req.user._id; + // Check if any required field is missing + // if (!ticketId || !createdOn || !subject || !description) { + // return res.status(400).json({ + // success: false, + // msg: "All fields are required.", + // }); + // } + + // Create the support ticket + // const support = await Support.create({ + // ticketId, + // createdOn, + // subject, + // description, + // addedBy: req.user._id, + // image: imagesLinks, + // }); + const support = await Support.create({ ...req.body }); + // Return the created support ticket + res.status(201).json({ + success: true, + data: support, + msg: "Support ticket created successfully.", + }); + } catch (error) { + console.error(error); + // Handle errors + res.status(500).json({ + success: false, + msg: error.message, + }); + } +}; +// **************************** + +export const getAllSupportTicket = async (req, res) => { + try { + // Use the find method to retrieve all support tickets + const support = await Support.find().sort({ createdAt: -1 }); + + // Check if support tickets were found + if (support) { + return res.status(200).json({ + success: true, + support, + }); + } + } catch (error) { + // Handle errors + // console.error(error); + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; +export const getOneSupportTicket = async (req, res) => { + try { + // console.log(req.params.id); + const support = await Support.findOne({ ticketId: req.params.id }); + if (support) { + return res.status(200).json({ + success: true, + support, + }); + } else { + return res.status(404).json({ + success: false, + msg: "Support ticket not found", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; + +// ************************8 + +export const getAllSupportTicketofuser = async (req, res) => { + try { + // Retrieve the user ID from the request + const userId = req.user._id; + + // Use the find method to retrieve all support tickets created by the user + const support = await Support.find({ addedBy: userId }).sort({ + createdAt: -1, + }); + + // Check if support tickets were found + if (support) { + return res.status(200).json({ + success: true, + support, + }); + } else { + return res.status(404).json({ + success: false, + msg: "No support tickets found for the user.", + }); + } + } catch (error) { + // Handle errors + // console.error(error); + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; + +// ************************8 + +export const deleteSupport = async (req, res) => { + try { + if (!req.params.id) { + return res.status(400).json({ + success: false, + msg: "Please Provide Support ID!", + }); + } + // console.log(req.params.id); + const getSupport = await Support.findById(req.params.id); + if (!getSupport) { + return res.status(404).json({ + success: false, + msg: "Support not Found!", + }); + } + // Deleting Images From Cloudinary + for (let i = 0; i < getSupport.image.length; i++) { + await cloudinary.v2.uploader.destroy(getSupport.image[i].public_id); + } + + //-------------------------// + const supportticket = await Support.findByIdAndDelete(req.params.id); + if (!supportticket) { + return res.status(404).json({ message: "Support Not Found" }); + } + await supportticket.remove(); + res.status(200).json({ + success: true, + msg: "Support Deleted Successfully!!", + }); + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; + +export const updateSupport = async (req, res) => { + try { + const { status, message } = req.body; + // console.log(req.params.id); + // Prepare an array for the images + // const jsonArray = JSON.parse(image); + // const AllImages = jsonArray.map(({ public_id, url }) => ({ + // public_id, + // url, + // })); + + // if (req.files && req.files.newImages) { + // const newuploadImages = Array.isArray(req.files.newImages) + // ? req.files.newImages + // : [req.files.newImages]; + + // const imagesLinks = []; + + // for (let i = 0; i < newuploadImages.length; i++) { + // const result = await cloudinary.v2.uploader.upload( + // newuploadImages[i].tempFilePath, + // { + // folder: "smellica/product", + // } + // ); + + // imagesLinks.push({ + // public_id: result.public_id, + // url: result.secure_url, + // }); + // } + + // Combine the existing images and the newly uploaded images + // const updatedImages = [...AllImages, ...imagesLinks]; + + // Perform the product update + // Find the support ticket by ID + const supportTicket = await Support.findOne({ ticketId: req.params.id }); + // Check if the support ticket exists + if (!supportTicket) { + return res.status(404).json({ + success: false, + msg: "Support ticket not found", + }); + } + + // Update the support ticket fields + if (status) { + supportTicket.status = status; + } + if (message) { + const newMessage = { + message: message.message, + user: message.user, + replyDate: message.replyDate, // Add a timestamp to the message object + }; + supportTicket.message.push(newMessage); + // Update the last reply to the timestamp of the new message if the user is admin + if (message.user === "admin") { + supportTicket.lastreply = newMessage.replyDate; + } + } + + // Save the updated support ticket + const updatedSupportTicket = await supportTicket.save(); + + return res.status(200).json({ + success: true, + updatedSupportTicket, + }); + } catch (error) { + // Handle errors + // console.error(error); + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; +export const deleteImageFromCloudinary = async (req, res) => { + const { public_id } = req.params; + + try { + if (!public_id) { + return res.status(400).json({ + success: false, + msg: "Please Provide Product ID!", + }); + } + const response = await cloudinary.v2.uploader.destroy(public_id); + if (response) { + res.status(200).json({ + success: true, + msg: "CustomerSupport Deleted Successfully!!", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; \ No newline at end of file diff --git a/resources/Supports/supportModel.js b/resources/Supports/supportModel.js new file mode 100644 index 0000000..25892b0 --- /dev/null +++ b/resources/Supports/supportModel.js @@ -0,0 +1,67 @@ +import mongoose from "mongoose"; +const { Schema, model } = mongoose; +const supportSchema = new Schema( + { + ticketId: { + type: String, + required: true, + unique: true, + }, + addedBy: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, + subject: { + type: String, + required: true, + }, + description: { + type: String, + maxLength: [100, "description cannot exceed 100 characters"], + required: [true, "Please Enter product Description"], + }, + createdOn: { + type: String, + }, + lastreply: { + type: String, + }, + status: { + type: String, + enum: ["Open", "Close"], + default: "Open", + }, + image: [ + { + public_id: { + type: String, + // required: true, + }, + url: { + type: String, + // required: true, + }, + }, + ], + message: [ + { + message: { + type: String, + default: "", + }, + user: { + type: String, + enum: ["admin", "user"], + default: "user", + }, + replyDate: { + type: String, + }, + }, + ], + }, + { timestamps: true, versionKey: false } +); + +export const Support = model("Support", supportSchema); diff --git a/resources/Supports/supportRoute.js b/resources/Supports/supportRoute.js new file mode 100644 index 0000000..dcc3c18 --- /dev/null +++ b/resources/Supports/supportRoute.js @@ -0,0 +1,32 @@ +import bodyParser from "body-parser"; +import { createSupport, deleteImageFromCloudinary, deleteSupport, getAllSupportTicket, getAllSupportTicketofuser, getOneSupportTicket, updateSupport } from "./supportController.js"; +import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; +import express from "express"; + +const app = express(); + +// Configure bodyParser to parse the raw request body as a buffer +app.use(bodyParser.raw({ type: "application/json" })); + +const router = express.Router(); +//checkout Routes-------------------------// +router.route("/support/create/").post(isAuthenticatedUser,createSupport); +router.route("/support/getAll/").get(getAllSupportTicket); +router.route("/support/userticket/").get(isAuthenticatedUser,getAllSupportTicketofuser); +router + .route("/support/delete/:id") + .delete( deleteSupport); + router.route("/support/getOne/:id").get(getOneSupportTicket); + router + .route("/support/update/:id") + .patch(updateSupport); + router + .route("/support/deleteImage/jatinMor/CustomerSupport/:public_id") + .delete( + isAuthenticatedUser, + authorizeRoles("admin"), + deleteImageFromCloudinary + ); +// --------------------------------------------------------- + +export default router; From bc6fe5d4ad86001ccb2515ace51a6cb197317053 Mon Sep 17 00:00:00 2001 From: roshangarg28 Date: Tue, 26 Mar 2024 10:41:33 +0530 Subject: [PATCH 07/15] place order with stripe done --- .env | 2 +- resources/Orders/StripeCheckOutController.js | 62 +++++++++++++++----- 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/.env b/.env index ed782b0..d787838 100644 --- a/.env +++ b/.env @@ -13,7 +13,7 @@ WEBHOOK_SECRET_KEY="whsec_m9u7CFBCY1qWarhxq65CkII6egOBf20K" STRIPE_SECRET_KEY="sk_test_51OhPRdSG6gbAOwcEid1GavJ4FTD0ZuHVTferdvJwKal77RlMtFJGBzL5GjtL0ie8ZJztsGjUWi8DWrnw1pDdDRGS005Hk0ahql" STRIPE_WEBHOOK_SECRET="whsec_dc9b9084fc764c806c8c5c06dd91de1ee809e9c8deab6d56e8e3ef2fc9c30c67" -FRONTEND_URL="http://127.0.0.1:5173" +FRONTEND_URL="https://smellika.com" SEND_EMAIL_FROM="hello@smellika.com" diff --git a/resources/Orders/StripeCheckOutController.js b/resources/Orders/StripeCheckOutController.js index 81f6417..f031fe1 100644 --- a/resources/Orders/StripeCheckOutController.js +++ b/resources/Orders/StripeCheckOutController.js @@ -79,7 +79,7 @@ export const handlePayment = async (req, res) => { shippingInfo: shipping, user: req.user._id, }); - console.log("fffffffff", order, "llllllllll"); + // console.log("fffffffff", order, "llllllllll"); const lineItems = await cart.map((item) => ({ price_data: { currency: "inr", @@ -108,8 +108,8 @@ export const handlePayment = async (req, res) => { // Allow only India for INR transactions }, billing_address_collection: "required", - success_url: "http://localhost:5173/order-complete", // Provide your success URL here - cancel_url: "http://localhost:5173/cart", + success_url: `${process.env.FRONTEND_URL}/order-complete`, // Provide your success URL here + cancel_url: `${process.env.FRONTEND_URL}/cart`, }); // res.json({ sessionId: session.id }); @@ -125,6 +125,7 @@ export const handlePayment = async (req, res) => { export const webhook = async (req, res) => { const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; + const signature = req.headers["stripe-signature"]; let event; if (webhookSecret) { @@ -142,7 +143,6 @@ export const webhook = async (req, res) => { } if (event.type === "checkout.session.completed") { - // console.log("dddddddddddd", event.data); const findOrder = await Order.findById(event.data.object.metadata?.orderId); findOrder.paypal_payer_id = event.data.object.id; findOrder.paidAt = new Date(event.data.object.created * 1000); @@ -154,20 +154,52 @@ export const webhook = async (req, res) => { } findOrder.orderStatus = "new"; await findOrder.save(); + + // Construct the HTML for the email + const itemRows = findOrder?.orderItems + .map( + (item) => + `${item?.name}${item?.quantity}₹${item?.price}` + ) + .join(""); + + const htmlContent = ` + + Hi ${findOrder?.shippingInfo?.first_Name}, + +

Great news! Your order #${findOrder?.orderID} has been confirmed. Here are the details:

+
+ + + + + + + + + + + ${itemRows} + +
ItemQuantityPrice
+ +

Shipping Address: ${findOrder?.shippingInfo.first_Name} ${findOrder?.shippingInfo.last_Name},${findOrder?.shippingInfo.postalCode}, + ${findOrder?.shippingInfo.street}, + ${findOrder?.shippingInfo.city}, + ${findOrder?.shippingInfo.state}, + ${findOrder?.shippingInfo.country}
Phone number:${findOrder?.shippingInfo.phone_Number}

+

Total: ₹${findOrder.total_amount}

+ +
+ Best regards,
+ Team Smellika`; + + // Send the email await sendEmail({ to: `${event.data.object.customer_email}`, // Change to your recipient - from: `${process.env.SEND_EMAIL_FROM}`, // Change to your verified sender - subject: `Your Order #${findOrder?.orderID} Confirmation`, - html: `

Welcome to Smellika - Let the Shopping Begin!

- Hi ${findOrder?.shippingInfo?.first_Name}, - -

Great news! Your order #${findOrder?.orderID} has been confirmed. Here are the details

-
- Best regards,
- - Team Smellika`, + html: htmlContent, }); // Items: [List of Purchased Items] @@ -184,7 +216,7 @@ export const webhook = async (req, res) => { "---------------------" ); - console.log(`💰 Payment status: ${event.data.object?.payment_status}`); + // console.log(`💰 Payment status: ${event.data.object?.payment_status}`); // Saving the payment details in the database // const payment = await Payment.create({ From c6ebde79f6229de49a80222b6c64a8ade869d21e Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Wed, 27 Mar 2024 10:49:58 +0530 Subject: [PATCH 08/15] Testimonial API for create,edit,delete is complete and fix small error in support section --- resources/Supports/supportRoute.js | 6 +- .../Testimonials/TestimonialController.js | 125 +++++++++++++++++- resources/Testimonials/TestimonialRoute.js | 19 ++- 3 files changed, 143 insertions(+), 7 deletions(-) diff --git a/resources/Supports/supportRoute.js b/resources/Supports/supportRoute.js index dcc3c18..3cacdb0 100644 --- a/resources/Supports/supportRoute.js +++ b/resources/Supports/supportRoute.js @@ -11,15 +11,15 @@ app.use(bodyParser.raw({ type: "application/json" })); const router = express.Router(); //checkout Routes-------------------------// router.route("/support/create/").post(isAuthenticatedUser,createSupport); -router.route("/support/getAll/").get(getAllSupportTicket); +router.route("/support/getAll/").get(isAuthenticatedUser, authorizeRoles("admin"),getAllSupportTicket); router.route("/support/userticket/").get(isAuthenticatedUser,getAllSupportTicketofuser); router .route("/support/delete/:id") .delete( deleteSupport); - router.route("/support/getOne/:id").get(getOneSupportTicket); + router.route("/support/getOne/:id").get(isAuthenticatedUser, getOneSupportTicket); router .route("/support/update/:id") - .patch(updateSupport); + .patch(isAuthenticatedUser, updateSupport); router .route("/support/deleteImage/jatinMor/CustomerSupport/:public_id") .delete( diff --git a/resources/Testimonials/TestimonialController.js b/resources/Testimonials/TestimonialController.js index 98798d8..31229d1 100644 --- a/resources/Testimonials/TestimonialController.js +++ b/resources/Testimonials/TestimonialController.js @@ -3,7 +3,7 @@ import { Testimonial } from "./TestimonialModel.js"; export const AddNewTestimonial = async (req, res) => { try { if (!req?.user) return res.status(400).json({ message: "please login !" }); - // console.log(req?.user) + // console.log(req?.user); if (req.files) { let getImg = req.files.image; @@ -68,6 +68,7 @@ export const FindOneTestimonial = async (req, res) => { return res.status(400).json({ message: "please give ID !" }); const testimonial = await Testimonial.findById(req.params.id); + // console.log(testimonial); if (testimonial) { return res.status(200).json({ success: true, @@ -82,3 +83,125 @@ export const FindOneTestimonial = async (req, res) => { }); } }; + +// 3.update testimonials +export const updatetesTimonial = async (req, res) => { + try { + // Check if the user is authenticated + if (!req.user) { + return res.status(400).json({ message: "Please login!" }); + } + + // Destructure request body + const { name, company, testimonial } = req.body; + + // Get the authenticated user's ID + const userId = req.user._id; + + // Prepare an object for the updated testimonial data + const updatedTestimonialData = { + name, + company, + testimonial, + user: userId, // Assign the authenticated user's ID to the testimonial's user field + }; + + // Check if files are uploaded + if (req.files && req.files.image) { + // If image file is uploaded, upload it to cloudinary + const uploadedImage = req.files.image; + const result = await cloudinary.v2.uploader.upload( + uploadedImage.tempFilePath, + { + folder: "GetSygnal/Testimonial", + } + ); + + // Prepare the image object with public_id and url + const image = { + public_id: result.public_id, + url: result.secure_url, + }; + + // Assign the uploaded image to the testimonial's image field + updatedTestimonialData.image = image; + } + // console.log(updatedTestimonialData); + // Update the testimonial in the database + const modifiedTestimonial = await Testimonial.findOneAndUpdate( + { _id: req.params.id }, + { $set: updatedTestimonialData }, + { new: true } + ); + + return res.status(200).json({ + success: true, + ModifyTestimonial: modifiedTestimonial, + }); + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; + +export const deleteImageFromCloudinary = async (req, res) => { + const { public_id } = req.params; + + try { + if (!public_id) { + return res.status(400).json({ + success: false, + msg: "Please Provide Product ID!", + }); + } + const response = await cloudinary.v2.uploader.destroy(public_id); + if (response) { + res.status(200).json({ + success: true, + msg: "Product Deleted Successfully!!", + }); + } + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; + +//delete one Product +export const deleteTestimonial = async (req, res) => { + try { + if (!req?.user) return res.status(400).json({ message: "please login !" }); + // console.log(req?.user) + if (!req.params.id) + return res.status(400).json({ message: "please give ID !" }); + // console.log(req.params.id) + const gettestimonial = await Testimonial.findById(req.params.id); + // console.log(gettestimonial) + if (!gettestimonial) { + return res + .status(404) + .json({ success: false, msg: "Testimonial not Found!" }); + } + // Deleting Images From Cloudinary + await cloudinary.v2.uploader.destroy(gettestimonial.image.public_id); + + //-------------------------// + const testimonial = await Testimonial.findByIdAndDelete(req.params.id); + if (!testimonial) { + return res.status(404).json({ message: "Testimonial Not Found" }); + } + await testimonial.remove(); + res + .status(200) + .json({ success: true, msg: "Testimonial Deleted Successfully!!" }); + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; diff --git a/resources/Testimonials/TestimonialRoute.js b/resources/Testimonials/TestimonialRoute.js index 90fec8c..6e80825 100644 --- a/resources/Testimonials/TestimonialRoute.js +++ b/resources/Testimonials/TestimonialRoute.js @@ -4,6 +4,9 @@ import { AddNewTestimonial, FindAllTestimonial, FindOneTestimonial, + deleteImageFromCloudinary, + deleteTestimonial, + updatetesTimonial, } from "./TestimonialController.js"; const router = express.Router(); @@ -13,7 +16,17 @@ router .route("/getAll") .get(isAuthenticatedUser, authorizeRoles("admin"), FindAllTestimonial); router.route("/getOne/:id").get(isAuthenticatedUser, FindOneTestimonial); - -// router.route("/product/getAll/").get(getAllProduct) - +router + .route("/delete/:id") + .delete(isAuthenticatedUser, authorizeRoles("admin"), deleteTestimonial); + router + .route("/update/:id") + .patch(isAuthenticatedUser, authorizeRoles("admin"), updatetesTimonial); + router + .route("/deleteImage/GetSygnal/Testimonial/:public_id") + .delete( + isAuthenticatedUser, + authorizeRoles("admin"), + deleteImageFromCloudinary + ); export default router; From 22fa115b4ce61884ad2629fbbb5ac17c3a52631f Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Wed, 27 Mar 2024 12:19:46 +0530 Subject: [PATCH 09/15] updated testimonial get all route --- resources/Testimonials/TestimonialRoute.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/Testimonials/TestimonialRoute.js b/resources/Testimonials/TestimonialRoute.js index 6e80825..9dda39f 100644 --- a/resources/Testimonials/TestimonialRoute.js +++ b/resources/Testimonials/TestimonialRoute.js @@ -14,7 +14,7 @@ const router = express.Router(); router.route("/new").post(isAuthenticatedUser, AddNewTestimonial); router .route("/getAll") - .get(isAuthenticatedUser, authorizeRoles("admin"), FindAllTestimonial); + .get(FindAllTestimonial); router.route("/getOne/:id").get(isAuthenticatedUser, FindOneTestimonial); router .route("/delete/:id") From fd6a8e9c9b0689c6078e41c242f723e9ca321368 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Wed, 27 Mar 2024 14:46:34 +0530 Subject: [PATCH 10/15] updated testimonial view all testimonial controller --- resources/Testimonials/TestimonialController.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/resources/Testimonials/TestimonialController.js b/resources/Testimonials/TestimonialController.js index 31229d1..2268ecf 100644 --- a/resources/Testimonials/TestimonialController.js +++ b/resources/Testimonials/TestimonialController.js @@ -36,8 +36,6 @@ export const AddNewTestimonial = async (req, res) => { export const FindAllTestimonial = async (req, res) => { try { - if (!req?.user) return res.status(400).json({ message: "please login !" }); - // console.log(req?.user) const testimonial = await Testimonial.find().sort({ createdAt: -1 }); if (testimonial) { From d294d2706b4fc12625520f616035bd553e81c402 Mon Sep 17 00:00:00 2001 From: Sibunnayak Date: Thu, 28 Mar 2024 09:53:01 +0530 Subject: [PATCH 11/15] category wise product find controller updated --- resources/Products/ProductController.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/resources/Products/ProductController.js b/resources/Products/ProductController.js index 887ed0c..777b8fa 100644 --- a/resources/Products/ProductController.js +++ b/resources/Products/ProductController.js @@ -1,7 +1,7 @@ import { Product } from "./ProductModel.js"; import cloudinary from "../../Utils/cloudinary.js"; import { v4 as uuidv4 } from "uuid"; - +import { CategoryModel } from "../Category/CategoryModel.js"; export const createProduct = async (req, res) => { try { if (!req.files) { @@ -240,13 +240,20 @@ export const deleteProduct = async (req, res) => { }); } }; + export const getProductsByCategory = async (req, res) => { const { categoryName } = req.params; // Assuming category name is in the route + // console.log(categoryName); try { - const products = await Product.find({ - category: categoryName, - }).sort({ createdAt: -1 }); + // Find the category object by name first + const category = await CategoryModel.findOne({ categoryName }); + + if (!category) { + throw new Error("Category not found"); + } + const products = await Product.find({ category: category._id }).populate('category'); + // console.log(products); if (products && products.length > 0) { return res.status(200).json({ From 608cc318a6f1212c074b71d3a72a5d11c04bdcea Mon Sep 17 00:00:00 2001 From: syedmujahidahmed Date: Thu, 28 Mar 2024 12:40:38 +0530 Subject: [PATCH 12/15] fixed legal content api --- resources/Content/ContentController.js | 128 +++++++++++++++++-------- resources/Content/ContentRoutes.js | 6 +- 2 files changed, 93 insertions(+), 41 deletions(-) diff --git a/resources/Content/ContentController.js b/resources/Content/ContentController.js index f7500ff..b366441 100644 --- a/resources/Content/ContentController.js +++ b/resources/Content/ContentController.js @@ -51,16 +51,24 @@ export const getTermsAndCondition = async (req, res) => { export const updateTermsAndConditions = async (req, res) => { try { if (!req?.user) return res.status(400).json({ message: "please login !" }); - // console.log(req?.user) - const { content } = req.body; - const termsAndCondition = await TermsAndCondition.findOneAndUpdate( - { - addedBy: req.user._id, - }, - { - termsAndContionContent: content, - } - ); + // new content + const { content } = req.body; + + // id of the terms and conndition document + const id = req.query.id; + + // object for updated terms and conndition data + const updatedTermsData = { + termsAndContionContent: content, + addedBy: req.user._id + } + + // update the terms and conndition in database + const termsAndCondition = await TermsAndCondition.findByIdAndUpdate( + { _id: id }, + { $set: updatedTermsData }, + { new: true } + ); res.status(200).json({ success: true, @@ -80,23 +88,10 @@ export const RefundPolicy = async (req, res) => { if (!req?.user) return res.status(400).json({ message: "please login !" }); // console.log(req?.user) const { content } = req.body; - const findv = await Refundpolicy.findOne(); - let refundPolicy; - if (findv) { - refundPolicy = await Refundpolicy.findOneAndUpdate( - { - addedBy: req.user._id, - }, - { - Refundpolicy: content, - } - ); - } else { - refundPolicy = await Refundpolicy.create({ + const refundPolicy = await Refundpolicy.create({ addedBy: req.user._id, Refundpolicy: content, }); - } res.status(200).json({ success: true, @@ -130,6 +125,42 @@ export const getRefundPolicy = async (req, res) => { } }; +// update refund policy +export const updateRefundPolicy = async (req, res) => { + try { + if (!req?.user) return res.status(400).json({ message: "please login !" }); + + const {content} = req.body; + // id of the refund policy document + const id = req.query.id; + + // object for updated refund policy data + const updatedRefundPolicyData = { + Refundpolicy: content, + addedBy: req.user._id + } + + // update the refund policy in database + const refundPolicy = await Refundpolicy.findByIdAndUpdate( + { _id: id }, + { $set: updatedRefundPolicyData }, + { new: true } + ); + + res.status(200).json({ + success: true, + refundPolicy, + message: "updated successfully ", + }); + } catch (error) { + res.status(500).json({ + success: false, + message: error.message ? error.message : "Something went Wrong", + }); + } +}; + + // Privacy policy controller functions export const AddPrivacyAndPolicy = async (req, res) => { @@ -180,15 +211,24 @@ export const getPrivacyPolicy = async (req, res) => { export const updatePrivacyPolicy = async (req, res) => { try { if (!req?.user) return res.status(400).json({ message: "please login !" }); - // console.log(req?.user) + + // new content const { content } = req.body; - const privacyAndPolicy = await PrivacyAndPolicy.findOneAndUpdate( - { - addedBy: req.user._id, - }, - { - privacyAndPolicyContent: content, - } + + // id of the privacy policy document + const id = req.query.id; + + // object for updated privacy policy data + const updatedPrivacyPolicyData = { + privacyAndPolicyContent: content, + addedBy: req.user._id + } + + // update the privacy policy in database + const privacyAndPolicy = await PrivacyAndPolicy.findByIdAndUpdate( + { _id: id }, + { $set: updatedPrivacyPolicyData }, + { new: true } ); res.status(200).json({ @@ -254,15 +294,23 @@ export const getShipping = async (req, res) => { export const updateShipping = async (req, res) => { try { if (!req?.user) return res.status(400).json({ message: "please login !" }); - // console.log(req?.user) + // new content const { content } = req.body; - const shipping = await Shipping.findOneAndUpdate( - { - addedBy: req.user._id, - }, - { - shippingContent: content, - } + + // id of the shipping policy document + const id = req.query.id; + + // object for updated shipping policy data + const updatedShippingData = { + shippingContent: content, + addedBy: req.user._id + } + + // update the shipping policy in database + const shipping = await Shipping.findByIdAndUpdate( + { _id: id }, + { $set: updatedShippingData }, + { new: true } ); res.status(200).json({ diff --git a/resources/Content/ContentRoutes.js b/resources/Content/ContentRoutes.js index 907dafe..eddc29a 100644 --- a/resources/Content/ContentRoutes.js +++ b/resources/Content/ContentRoutes.js @@ -11,6 +11,7 @@ import { updatePrivacyPolicy, updateShipping, updateTermsAndConditions, + updateRefundPolicy } from "./ContentController.js"; import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; @@ -46,7 +47,10 @@ router router.route("/refund-policy").get(getRefundPolicy); router .route("/refund-policy") - .patch(isAuthenticatedUser, authorizeRoles("admin"), RefundPolicy); + .post(isAuthenticatedUser, authorizeRoles("admin"), RefundPolicy); +router + .route("/refund-policy-update") + .patch(isAuthenticatedUser, authorizeRoles("admin"), updateRefundPolicy); // export default router; From 76166c19950eeb3ab595b5da1be4cb80cc411ed9 Mon Sep 17 00:00:00 2001 From: Raj-varu Date: Thu, 28 Mar 2024 15:42:40 +0530 Subject: [PATCH 13/15] add Blog Routes and controllers --- app.js | 4 ++ resources/Blog/BlogController.js | 63 ++++++++++++++++++++++++++++++++ resources/Blog/BlogModel.js | 28 ++++++++++++++ resources/Blog/BlogRoute.js | 11 ++++++ resources/Blog/dummy.json | 5 +++ 5 files changed, 111 insertions(+) create mode 100644 resources/Blog/BlogController.js create mode 100644 resources/Blog/BlogModel.js create mode 100644 resources/Blog/BlogRoute.js create mode 100644 resources/Blog/dummy.json diff --git a/app.js b/app.js index 701ad0d..1396269 100644 --- a/app.js +++ b/app.js @@ -161,6 +161,8 @@ import SeoRoute from "./resources/SEO&Analytics/SEORouter.js"; //Affiliate Routes import AffiliateRoute from "./resources/Affiliate&Coupon/Affiliate/AffiliateRoute.js"; +//Blog Routes +import BlogRoute from "./resources/Blog/BlogRoute.js"; //Coupon Routes import CouponRoute from "./resources/Affiliate&Coupon/Coupon/CouponRoute.js"; //short urls @@ -218,6 +220,8 @@ app.use("/api/v1/affiliate", AffiliateRoute); //Coupons app.use("/api/v1/coupon", CouponRoute); +//Blog +app.use("/api/v1/blog", BlogRoute); //config specialty // app.use("/api/config/specialty", SpecialtiesRouter); //specialties diff --git a/resources/Blog/BlogController.js b/resources/Blog/BlogController.js new file mode 100644 index 0000000..79abfd3 --- /dev/null +++ b/resources/Blog/BlogController.js @@ -0,0 +1,63 @@ +import Blog from "./BlogModel.js"; + +export const createBlog = async (req, res) => { + const { title, tags, image, blog_content } = req.body; + console.log(req.body); + + // Checking Fields + if (!title || !tags || !image || !blog_content) { + return res.status(400).json({ + success: false, + message: "All fields are mandatory", + }); + } + + try { + let images = []; + let Allfiles = req.files.image; + console.log(Allfiles); + // if (!Array.isArray(Allfiles)) { + // Allfiles = [Allfiles]; // Convert to array if it's not already + // } + + // Allfiles.forEach((file) => { + // if (typeof file.tempFilePath === "string") { + // let filepath = file.tempFilePath; + // images.push(filepath); + // } + // }); + + // const newBlog = await Blog.create({ + // title, + // tags, + // image: images, // Assign the array of image file paths + // blog_content, + // }); + + res.status(201).json({ + success: true, + message: "Blog created successfully", + data: images, + }); + } catch (error) { + console.error("Error creating blog:", error); + res.status(500).json({ + success: false, + message: error.message ? error.message : "Internal server error", + }); + } +}; +export const getAllBlog = async (req, res) => { + try { + const saveData = await Blog.find(); + res.status(200).json({ + success: true, + message: saveData, + }); + } catch { + res.status(500).json({ + success: false, + message: "Internal server error", + }); + } +}; diff --git a/resources/Blog/BlogModel.js b/resources/Blog/BlogModel.js new file mode 100644 index 0000000..1b3a65b --- /dev/null +++ b/resources/Blog/BlogModel.js @@ -0,0 +1,28 @@ +import mongoose from "mongoose"; + +const { Schema, model } = mongoose; + +const blogSchema = new Schema( + { + title: { + type: String, + required: [true, "Title is required"], + }, + tags: { + type: [String], + required: [true, "Tags are required"], + }, + image: { + type: String, + }, + blog_content: { + type: Object, + required: [true, "Content is required"], + }, + }, + { timestamps: true } +); + +const Blog = model("Blog", blogSchema); + +export default Blog; diff --git a/resources/Blog/BlogRoute.js b/resources/Blog/BlogRoute.js new file mode 100644 index 0000000..772b7b6 --- /dev/null +++ b/resources/Blog/BlogRoute.js @@ -0,0 +1,11 @@ +import express from "express"; + +import { createBlog, getAllBlog } from "./BlogController.js"; +import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; + +const router = express.Router(); + +router.post("/create", createBlog); +router.get("/getallblog", getAllBlog); + +export default router; diff --git a/resources/Blog/dummy.json b/resources/Blog/dummy.json new file mode 100644 index 0000000..90dfec3 --- /dev/null +++ b/resources/Blog/dummy.json @@ -0,0 +1,5 @@ +[ + { + "title": "String0" + } +] From 5d6093ca6e46e4772903f147fe0c9fbbd854bb1d Mon Sep 17 00:00:00 2001 From: roshangarg28 Date: Fri, 29 Mar 2024 16:28:43 +0530 Subject: [PATCH 14/15] product contorller apis updated --- resources/Products/ProductController.js | 178 ++++++++++++++++++------ resources/Products/ProductModel.js | 4 +- 2 files changed, 138 insertions(+), 44 deletions(-) diff --git a/resources/Products/ProductController.js b/resources/Products/ProductController.js index 887ed0c..02799f3 100644 --- a/resources/Products/ProductController.js +++ b/resources/Products/ProductController.js @@ -98,8 +98,109 @@ export const getOneProduct = async (req, res) => { }; // 3.update Product +// export const updateProduct = async (req, res) => { +// const { +// name, +// description, +// price, +// category, +// image, +// gst_amount, +// gst, +// total_amount, +// } = req.body; +// console.log(gst_amount, gst, total_amount); + +// try { +// // Prepare an array for the images +// const jsonArray = JSON.parse(image); +// const AllImages = jsonArray.map(({ public_id, url }) => ({ +// public_id, +// url, +// })); + +// if (req.files && req.files.newImages) { +// const newuploadImages = Array.isArray(req.files.newImages) +// ? req.files.newImages +// : [req.files.newImages]; + +// const imagesLinks = []; + +// for (let i = 0; i < newuploadImages.length; i++) { +// const result = await cloudinary.v2.uploader.upload( +// newuploadImages[i].tempFilePath, +// { +// folder: "smellica/product", +// } +// ); + +// imagesLinks.push({ +// public_id: result.public_id, +// url: result.secure_url, +// }); +// } + +// // Combine the existing images and the newly uploaded images +// const updatedImages = [...AllImages, ...imagesLinks]; + +// // Perform the product update +// const ModifyProduct = await Product.findOneAndUpdate( +// { _id: req.params.id }, +// { +// $set: { +// name, +// description, +// price, +// category, +// image: updatedImages, +// gst, +// gst_amount, +// total_amount, +// }, +// }, +// { new: true } +// ); +// return res.status(200).json({ +// success: true, +// ModifyProduct, +// }); +// } else { +// const ModifyProduct = await Product.findOneAndUpdate( +// { _id: req.params.id }, +// { +// $set: { +// name, +// description, +// price, +// category, +// image: AllImages, +// }, +// }, +// { new: true } +// ); +// return res.status(200).json({ +// success: true, +// ModifyProduct, +// }); +// } +// } catch (error) { +// res.status(500).json({ +// success: false, +// msg: error.message ? error.message : "Something went wrong!", +// }); +// } +// }; export const updateProduct = async (req, res) => { - const { name, description, price, category, image } = req.body; + const { + name, + description, + price, + category, + image, + gst_amount, + gst, + total_amount, + } = req.body; try { // Prepare an array for the images @@ -109,16 +210,18 @@ export const updateProduct = async (req, res) => { url, })); + let updatedImages = AllImages; + if (req.files && req.files.newImages) { - const newuploadImages = Array.isArray(req.files.newImages) + const newUploadImages = Array.isArray(req.files.newImages) ? req.files.newImages : [req.files.newImages]; const imagesLinks = []; - for (let i = 0; i < newuploadImages.length; i++) { + for (let i = 0; i < newUploadImages.length; i++) { const result = await cloudinary.v2.uploader.upload( - newuploadImages[i].tempFilePath, + newUploadImages[i].tempFilePath, { folder: "smellica/product", } @@ -131,46 +234,37 @@ export const updateProduct = async (req, res) => { } // Combine the existing images and the newly uploaded images - const updatedImages = [...AllImages, ...imagesLinks]; - - // Perform the product update - const ModifyProduct = await Product.findOneAndUpdate( - { _id: req.params.id }, - { - $set: { - name, - description, - price, - category, - image: updatedImages, - }, - }, - { new: true } - ); - return res.status(200).json({ - success: true, - ModifyProduct, - }); - } else { - const ModifyProduct = await Product.findOneAndUpdate( - { _id: req.params.id }, - { - $set: { - name, - description, - price, - category, - image: AllImages, - }, - }, - { new: true } - ); - return res.status(200).json({ - success: true, - ModifyProduct, - }); + updatedImages = [...AllImages, ...imagesLinks]; } + + // Perform the product update + const updatedProduct = await Product.findOneAndUpdate( + { _id: req.params.id }, + { + $set: { + name, + description, + price, + category, + image: updatedImages, + gst, + gst_amount, + total_amount, + }, + }, + { new: true } + ); + + if (!updatedProduct) { + return res.status(404).json({ success: false, msg: "Product not found" }); + } + + return res.status(200).json({ + success: true, + updatedProduct, + }); } catch (error) { + console.error("Error updating product:", error); res.status(500).json({ success: false, msg: error.message ? error.message : "Something went wrong!", diff --git a/resources/Products/ProductModel.js b/resources/Products/ProductModel.js index 24469a4..4780c94 100644 --- a/resources/Products/ProductModel.js +++ b/resources/Products/ProductModel.js @@ -5,7 +5,7 @@ const productSchema = new Schema( { name: { type: String, - maxLength: [25, "name cannot exceed 25 characters"], + maxLength: [35, "name cannot exceed 25 characters"], required: [true, "Please Enter product Name"], trim: true, }, @@ -15,7 +15,7 @@ const productSchema = new Schema( }, description: { type: String, - maxLength: [100, "description cannot exceed 100 characters"], + maxLength: [400, "description cannot exceed 100 characters"], required: [true, "Please Enter product Description"], }, price: { From 9f56371ff9fb227011ef02398d5b3e9fe6d37b4f Mon Sep 17 00:00:00 2001 From: syedmujahidahmed Date: Fri, 29 Mar 2024 17:19:56 +0530 Subject: [PATCH 15/15] added getAllProducts api --- resources/Products/ProductController.js | 42 +++++++++++++++++++++++++ resources/Products/ProductRoute.js | 2 ++ 2 files changed, 44 insertions(+) diff --git a/resources/Products/ProductController.js b/resources/Products/ProductController.js index d1de412..9fb4748 100644 --- a/resources/Products/ProductController.js +++ b/resources/Products/ProductController.js @@ -75,6 +75,48 @@ export const getAllProduct = async (req, res) => { }); } }; +// get all product with device products first +export const getAllProductsDevicesFirst = async (req, res) => { + try { + // we want products with category name Device to be displayed first, so i have first found the products with category name Devices then made another request to find all products and filtered products with category devices , then merged both arrays so we get devices first then all other categories + + const categoryName = 'Devices'; + // Find the category object by name first + const category = await CategoryModel.findOne({ categoryName }); + + if (!category) { + throw new Error("Category not found"); + } + // products with device category + const deviceProducts = await Product.find({ category: category._id }).populate('category'); + + // all products + const allProducts = await Product.find() + .populate({ + path: "category gst addedBy", + select: "name categoryName tax", + }) + .sort({ + createdAt: -1, + }); + // filtering out products with device category + const filteredProducts = allProducts.filter((ele) => { return ele.category?.categoryName !== categoryName }) + + // merging both deviceProcuts and filtered products + const product = deviceProducts.concat(filteredProducts) + if (product) { + return res.status(200).json({ + success: true, + product, + }); + } + } catch (error) { + res.status(500).json({ + success: false, + msg: error.message ? error.message : "Something went wrong!", + }); + } +}; //get One Product export const getOneProduct = async (req, res) => { try { diff --git a/resources/Products/ProductRoute.js b/resources/Products/ProductRoute.js index 220f4e3..2159d1f 100644 --- a/resources/Products/ProductRoute.js +++ b/resources/Products/ProductRoute.js @@ -7,6 +7,7 @@ import { getOneProduct, deleteImageFromCloudinary, getProductsByCategory, + getAllProductsDevicesFirst, } from "./ProductController.js"; const router = express.Router(); import { isAuthenticatedUser, authorizeRoles } from "../../middlewares/auth.js"; @@ -14,6 +15,7 @@ router .route("/product/create/") .post(isAuthenticatedUser, authorizeRoles("admin"), createProduct); router.route("/product/getAll/").get(getAllProduct); +router.route("/product/getAllProductsDevicesFrist/").get(getAllProductsDevicesFirst); router.route("/product/getOne/:id").get(getOneProduct); router .route("/product/update/:id")