Compare commits
No commits in common. "4e1ace0586ded60fae7372c6b4339edca3a00286" and "a5fc4bd03c4b4762b38dba8de0a29fbb6da697f2" have entirely different histories.
4e1ace0586
...
a5fc4bd03c
9 changed files with 5 additions and 233 deletions
|
@ -3,4 +3,3 @@ DB_USERNAME=root
|
||||||
DB_PASSWORD=password
|
DB_PASSWORD=password
|
||||||
JWT_SECRET=your_secret_key
|
JWT_SECRET=your_secret_key
|
||||||
DATABASE_URL="URI"
|
DATABASE_URL="URI"
|
||||||
SENDGRID_API_KEY="API_KEY"
|
|
||||||
|
|
71
package-lock.json
generated
71
package-lock.json
generated
|
@ -9,7 +9,6 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sendgrid/mail": "^7.7.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"dotenv": "^16.1.4",
|
"dotenv": "^16.1.4",
|
||||||
|
@ -26,41 +25,6 @@
|
||||||
"nodemon": "^2.0.22"
|
"nodemon": "^2.0.22"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@sendgrid/client": {
|
|
||||||
"version": "7.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sendgrid/client/-/client-7.7.0.tgz",
|
|
||||||
"integrity": "sha512-SxH+y8jeAQSnDavrTD0uGDXYIIkFylCo+eDofVmZLQ0f862nnqbC3Vd1ej6b7Le7lboyzQF6F7Fodv02rYspuA==",
|
|
||||||
"dependencies": {
|
|
||||||
"@sendgrid/helpers": "^7.7.0",
|
|
||||||
"axios": "^0.26.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "6.* || 8.* || >=10.*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sendgrid/helpers": {
|
|
||||||
"version": "7.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sendgrid/helpers/-/helpers-7.7.0.tgz",
|
|
||||||
"integrity": "sha512-3AsAxfN3GDBcXoZ/y1mzAAbKzTtUZ5+ZrHOmWQ279AuaFXUNCh9bPnRpN504bgveTqoW+11IzPg3I0WVgDINpw==",
|
|
||||||
"dependencies": {
|
|
||||||
"deepmerge": "^4.2.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@sendgrid/mail": {
|
|
||||||
"version": "7.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@sendgrid/mail/-/mail-7.7.0.tgz",
|
|
||||||
"integrity": "sha512-5+nApPE9wINBvHSUxwOxkkQqM/IAAaBYoP9hw7WwgDNQPxraruVqHizeTitVtKGiqWCKm2mnjh4XGN3fvFLqaw==",
|
|
||||||
"dependencies": {
|
|
||||||
"@sendgrid/client": "^7.7.0",
|
|
||||||
"@sendgrid/helpers": "^7.7.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "6.* || 8.* || >=10.*"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@types/bcryptjs": {
|
"node_modules/@types/bcryptjs": {
|
||||||
"version": "2.4.2",
|
"version": "2.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
|
"resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.2.tgz",
|
||||||
|
@ -221,14 +185,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||||
},
|
},
|
||||||
"node_modules/axios": {
|
|
||||||
"version": "0.26.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz",
|
|
||||||
"integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
|
|
||||||
"dependencies": {
|
|
||||||
"follow-redirects": "^1.14.8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -420,14 +376,6 @@
|
||||||
"ms": "2.0.0"
|
"ms": "2.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/deepmerge": {
|
|
||||||
"version": "4.3.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz",
|
|
||||||
"integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=0.10.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/depd": {
|
"node_modules/depd": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||||
|
@ -560,25 +508,6 @@
|
||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/follow-redirects": {
|
|
||||||
"version": "1.15.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
|
|
||||||
"integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "individual",
|
|
||||||
"url": "https://github.com/sponsors/RubenVerborgh"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=4.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"debug": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/forwarded": {
|
"node_modules/forwarded": {
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sendgrid/mail": "^7.7.0",
|
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie-parser": "^1.4.6",
|
"cookie-parser": "^1.4.6",
|
||||||
"dotenv": "^16.1.4",
|
"dotenv": "^16.1.4",
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
import { Request, Response } from 'express';
|
|
||||||
import { Cart, ICart, Product, Order, IOrder } from '../mongoose/Schema';
|
|
||||||
import { sendEmailasync } from '../services/sendGrid';
|
|
||||||
import { config } from 'dotenv';
|
|
||||||
|
|
||||||
config();
|
|
||||||
|
|
||||||
export async function addToCart(req: Request, res: Response) {
|
|
||||||
try {
|
|
||||||
const { userId, productId } = req.body;
|
|
||||||
if (!productId) {
|
|
||||||
res.status(400).json({ error: 'Product id is required.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let cart: ICart = await Cart.findOne({ userId });
|
|
||||||
if (!cart) {
|
|
||||||
cart = await Cart.create({
|
|
||||||
userId,
|
|
||||||
products: { [productId]: 1 },
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
const currentQuantity = cart.products[productId];
|
|
||||||
if (currentQuantity === undefined) {
|
|
||||||
cart.products[productId] = 1;
|
|
||||||
} else {
|
|
||||||
cart.products[productId] += 1;
|
|
||||||
}
|
|
||||||
cart.markModified('products');
|
|
||||||
}
|
|
||||||
|
|
||||||
await cart.save();
|
|
||||||
res.status(200).json(cart);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error adding product to cart:', error);
|
|
||||||
res.status(500).json({ error: 'An error occurred while adding the product to the cart.' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function listCart(req: Request, res: Response) {
|
|
||||||
try {
|
|
||||||
const { userId } = req.body;
|
|
||||||
const cart = await Cart.findOne({ userId });
|
|
||||||
if (!cart) {
|
|
||||||
res.status(404).json({ error: 'Cart not found.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.status(200).json(cart.products);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error listing cart:', error);
|
|
||||||
res.status(500).json({ error: 'An error occurred while listing the cart.' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function checkout(req: Request, res: Response) {
|
|
||||||
|
|
||||||
const { userId } = req.body;
|
|
||||||
const usersCart = await Cart.findOne({ userId });
|
|
||||||
console.log(usersCart)
|
|
||||||
if (!usersCart) {
|
|
||||||
res.status(404).json({ error: 'Cart not found.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const order = await Order.create({
|
|
||||||
userId,
|
|
||||||
products: usersCart.products,
|
|
||||||
});
|
|
||||||
|
|
||||||
await removeCart(userId);
|
|
||||||
sendEmailasync(order.id, userId);
|
|
||||||
res.status(200).json(order);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function removeCart(userId: string) {
|
|
||||||
await Cart.deleteOne({ userId });
|
|
||||||
}
|
|
|
@ -4,10 +4,6 @@ import { Product, IProduct } from '../mongoose/Schema';
|
||||||
export async function createProduct(req: Request, res: Response) {
|
export async function createProduct(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { name, description, price, userId } = req.body;
|
const { name, description, price, userId } = req.body;
|
||||||
if(!name || !description || !price || !userId) {
|
|
||||||
res.status(400).json({ error: 'Name, description, price and user id are required.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const product: IProduct = await Product.create({
|
const product: IProduct = await Product.create({
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
|
@ -36,3 +32,5 @@ export async function listProducts(req: Request, res: Response) {
|
||||||
res.status(500).json({ error: 'An error occurred while listing the products.' });
|
res.status(500).json({ error: 'An error occurred while listing the products.' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@ import cookieParser from 'cookie-parser';
|
||||||
|
|
||||||
import userRouter from './routes/user';
|
import userRouter from './routes/user';
|
||||||
import productRouter from './routes/product';
|
import productRouter from './routes/product';
|
||||||
import cartRouter from './routes/cart';
|
|
||||||
|
|
||||||
const env = require('dotenv').config().parsed;
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
|
@ -21,10 +20,7 @@ mongoose.connect(env.DATABASE_URL);
|
||||||
const db = mongoose.connection;
|
const db = mongoose.connection;
|
||||||
|
|
||||||
// Check for DB connection
|
// Check for DB connection
|
||||||
db.on('error', () => {
|
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
|
||||||
console.error.bind(console, 'MongoDB connection error:')
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
db.once('open', () => {
|
db.once('open', () => {
|
||||||
console.log('Connected to MongoDB');
|
console.log('Connected to MongoDB');
|
||||||
});
|
});
|
||||||
|
@ -32,7 +28,6 @@ db.once('open', () => {
|
||||||
// Routes
|
// Routes
|
||||||
app.use('/users', userRouter);
|
app.use('/users', userRouter);
|
||||||
app.use('/products', productRouter);
|
app.use('/products', productRouter);
|
||||||
app.use('/cart', cartRouter);
|
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
|
|
|
@ -19,21 +19,6 @@ export interface IProduct extends Document {
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICart extends Document {
|
|
||||||
userId: string;
|
|
||||||
products: { [itemId: string]: number };
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IOrder extends Document {
|
|
||||||
userId: string;
|
|
||||||
products: { [itemId: string]: number };
|
|
||||||
emailSent: boolean;
|
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
}
|
|
||||||
|
|
||||||
const UserSchema: Schema = new Schema({
|
const UserSchema: Schema = new Schema({
|
||||||
firstName: { type: String, required: true },
|
firstName: { type: String, required: true },
|
||||||
lastName: { type: String, required: true },
|
lastName: { type: String, required: true },
|
||||||
|
@ -48,22 +33,7 @@ const ProductSchema: Schema = new Schema({
|
||||||
name: { type: String, required: true },
|
name: { type: String, required: true },
|
||||||
description: { type: String, required: true },
|
description: { type: String, required: true },
|
||||||
price: { type: Number, required: true },
|
price: { type: Number, required: true },
|
||||||
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
userId: { type: Schema.Types.ObjectId, ref: 'User' , required: true},
|
||||||
createdAt: { type: Date, default: Date.now },
|
|
||||||
updatedAt: { type: Date, default: Date.now },
|
|
||||||
});
|
|
||||||
|
|
||||||
const CartSchema: Schema = new Schema({
|
|
||||||
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true, unique: true },
|
|
||||||
products: { type: Schema.Types.Mixed, default: {} },
|
|
||||||
createdAt: { type: Date, default: Date.now },
|
|
||||||
updatedAt: { type: Date, default: Date.now },
|
|
||||||
});
|
|
||||||
|
|
||||||
const OrderSchema: Schema = new Schema({
|
|
||||||
userId: { type: Schema.Types.ObjectId, ref: 'User', required: true },
|
|
||||||
products: { type: Schema.Types.Mixed, default: {} },
|
|
||||||
emailSent: { type: Boolean, default: false },
|
|
||||||
createdAt: { type: Date, default: Date.now },
|
createdAt: { type: Date, default: Date.now },
|
||||||
updatedAt: { type: Date, default: Date.now },
|
updatedAt: { type: Date, default: Date.now },
|
||||||
});
|
});
|
||||||
|
@ -72,7 +42,5 @@ ProductSchema.index({ name: 1, userId: 1 }, { unique: true });
|
||||||
|
|
||||||
const User = mongoose.model<IUser>('User', UserSchema);
|
const User = mongoose.model<IUser>('User', UserSchema);
|
||||||
const Product = mongoose.model<IProduct>('Product', ProductSchema);
|
const Product = mongoose.model<IProduct>('Product', ProductSchema);
|
||||||
const Cart = mongoose.model<ICart>('Cart', CartSchema);
|
|
||||||
const Order = mongoose.model<IOrder>('Order', OrderSchema);
|
|
||||||
|
|
||||||
export { User, Product, Cart, Order };
|
export { User, Product };
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
import express, { Request } from 'express';
|
|
||||||
import { authenticateToken } from '../middlewares/checkAuth';
|
|
||||||
import { addToCart, listCart, checkout } from '../controllers/CartController';
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const cartRouter = express.Router();
|
|
||||||
|
|
||||||
cartRouter.post('/', authenticateToken, addToCart);
|
|
||||||
cartRouter.get('/', authenticateToken, listCart);
|
|
||||||
|
|
||||||
cartRouter.post('/checkout', authenticateToken, checkout);
|
|
||||||
|
|
||||||
export default cartRouter;
|
|
|
@ -1,26 +0,0 @@
|
||||||
import { config } from "dotenv";
|
|
||||||
import { User, Order } from "../mongoose/Schema";
|
|
||||||
import client from '@sendgrid/mail';
|
|
||||||
config();
|
|
||||||
|
|
||||||
export async function sendEmailasync(orderId:string, userId: string) {
|
|
||||||
console.log(`Sending email for order ${orderId} to user ${userId}`);
|
|
||||||
try {
|
|
||||||
client.setApiKey(process.env.SENDGRID_API_KEY);
|
|
||||||
const msg = {
|
|
||||||
to: await User.findOne({ _id: userId }).select('email'),
|
|
||||||
from: process.env.EMAIL_FROM,
|
|
||||||
subject: 'Order Confirmation',
|
|
||||||
text: 'Your order has been placed',
|
|
||||||
html: '<strong>Thank you for shopping with us!</strong>'
|
|
||||||
};
|
|
||||||
await client.send(msg);
|
|
||||||
console.log(`Email sent for order ${orderId} to user ${userId}`);
|
|
||||||
// Update the order to indicate email has been sent
|
|
||||||
await Order.findOne({ _id: orderId }).updateOne({ emailSent: true });
|
|
||||||
console.log(`Updated order ${orderId} to indicate email has been sent`);
|
|
||||||
return;
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error sending email:', error);
|
|
||||||
}
|
|
||||||
};
|
|
Loading…
Reference in a new issue