Compare commits
No commits in common. "e44d9f0b6f7fb353f87ddf04b3fe6903280cc08b" and "4e1ace0586ded60fae7372c6b4339edca3a00286" have entirely different histories.
e44d9f0b6f
...
4e1ace0586
12 changed files with 25 additions and 176 deletions
|
@ -1,2 +0,0 @@
|
||||||
node_modules
|
|
||||||
npm-debug.log
|
|
13
Dockerfile
13
Dockerfile
|
@ -1,13 +0,0 @@
|
||||||
FROM node:14-alpine
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY package*.json ./
|
|
||||||
|
|
||||||
RUN npm install --production
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
EXPOSE 3000
|
|
||||||
|
|
||||||
CMD ["npm", "start"]
|
|
98
README.md
98
README.md
|
@ -1,98 +0,0 @@
|
||||||
# Ecomm #
|
|
||||||
|
|
||||||
## Description ##
|
|
||||||
This is a simple e-commerce backend application.
|
|
||||||
|
|
||||||
Technologies used:
|
|
||||||
|
|
||||||
* Node.js
|
|
||||||
* typescript
|
|
||||||
* Express.js
|
|
||||||
* MongoDB
|
|
||||||
* Mongoose ORM
|
|
||||||
* Docker (docker-compose)
|
|
||||||
* bcrypt
|
|
||||||
* JWT
|
|
||||||
|
|
||||||
## How to run ##
|
|
||||||
1. Clone the repository
|
|
||||||
2. Make sure you have docker and docker-compose installed
|
|
||||||
3. Implement the .env file
|
|
||||||
4. Run `docker-compose up` in the root directory
|
|
||||||
The application will be running on port 3000
|
|
||||||
The database will be running on port 27017
|
|
||||||
|
|
||||||
## API ##
|
|
||||||
app.use('/products', productRouter);
|
|
||||||
app.use('/cart', cartRouter);
|
|
||||||
|
|
||||||
# Users #
|
|
||||||
## POST /users ## - Create a new user
|
|
||||||
### Request body ###
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "string",
|
|
||||||
"email": "string",
|
|
||||||
"password": "string"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
### Response body ###
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"user": {
|
|
||||||
"_id": "string",
|
|
||||||
"name": "string",
|
|
||||||
"email": "string",
|
|
||||||
"password": "string",
|
|
||||||
"createdAt": "string",
|
|
||||||
"updatedAt": "string",
|
|
||||||
"__v": "number"
|
|
||||||
},
|
|
||||||
"token": "string"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
## POST /users/login ## - Login
|
|
||||||
### Request body ###
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"email": "string",
|
|
||||||
"password": "string"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
### Response ###
|
|
||||||
# body #
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"access-token": "string"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
# headers #
|
|
||||||
``` Cookie - "access-token"": "string" ```
|
|
||||||
|
|
||||||
|
|
||||||
# Products #
|
|
||||||
## GET /products ## - Get all products
|
|
||||||
accepts query params: page, limit. Default values: page = 0, limit = 50.
|
|
||||||
### Response body ###
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"products": [
|
|
||||||
{
|
|
||||||
"_id": "string",
|
|
||||||
"name": "string",
|
|
||||||
"description": "string",
|
|
||||||
"price": "number",
|
|
||||||
"createdAt": "string",
|
|
||||||
"updatedAt": "string",
|
|
||||||
"__v": "number"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"total": "number"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,5 @@
|
||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
app:
|
|
||||||
build:
|
|
||||||
context: .
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
ports:
|
|
||||||
- 3000:3000
|
|
||||||
volumes:
|
|
||||||
- ./src:/app/src
|
|
||||||
- ./dist:/app/dist
|
|
||||||
depends_on:
|
|
||||||
- mongodb
|
|
||||||
mongodb:
|
mongodb:
|
||||||
image: arm64v8/mongo:4.0
|
image: arm64v8/mongo:4.0
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -20,11 +9,11 @@ services:
|
||||||
- structshare_vol:/data/db
|
- structshare_vol:/data/db
|
||||||
- ./init-scripts/init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
- ./init-scripts/init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||||
environment:
|
environment:
|
||||||
- MONGO_INITDB_DATABASE=your-database-name
|
- MONGO_INITDB_DATABASE=${DB_DATABASE}
|
||||||
- MONGO_INITDB_ROOT_USERNAME=your-username
|
- MONGO_INITDB_ROOT_USERNAME=${DB_USERNAME}
|
||||||
- MONGO_INITDB_ROOT_PASSWORD=your-password
|
- MONGO_INITDB_ROOT_PASSWORD=${DB_PASSWORD}
|
||||||
platform: linux/arm64/v8
|
platform: linux/arm64/v8
|
||||||
expose:
|
expose:
|
||||||
- 27017
|
- 27017
|
||||||
volumes:
|
volumes:
|
||||||
structshare_vol:
|
structshare_vol:
|
|
@ -5,8 +5,7 @@
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon dist/index.js",
|
"dev": "nodemon dist/index.js",
|
||||||
"start": "node dist/index.js",
|
"start": "node dist/index.js"
|
||||||
"build": "tsc -p ."
|
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Request, Response } from 'express';
|
import { Request, Response } from 'express';
|
||||||
import { Cart, ICart, Order } from '../mongoose/Schema';
|
import { Cart, ICart, Product, Order, IOrder } from '../mongoose/Schema';
|
||||||
import { sendEmailasync } from '../services/sendGrid';
|
import { sendEmailasync } from '../services/sendGrid';
|
||||||
import { config } from 'dotenv';
|
import { config } from 'dotenv';
|
||||||
|
|
||||||
|
@ -55,7 +55,7 @@ export async function checkout(req: Request, res: Response) {
|
||||||
|
|
||||||
const { userId } = req.body;
|
const { userId } = req.body;
|
||||||
const usersCart = await Cart.findOne({ userId });
|
const usersCart = await Cart.findOne({ userId });
|
||||||
|
console.log(usersCart)
|
||||||
if (!usersCart) {
|
if (!usersCart) {
|
||||||
res.status(404).json({ error: 'Cart not found.' });
|
res.status(404).json({ error: 'Cart not found.' });
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -5,13 +5,7 @@ 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) {
|
if(!name || !description || !price || !userId) {
|
||||||
res.status(400).json({ error: 'Name, description, price are required.' });
|
res.status(400).json({ error: 'Name, description, price and user id are required.' });
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const productExists = await Product.exists({ name, userId });
|
|
||||||
if(productExists) {
|
|
||||||
res.status(400).json({ error: 'Product already exists.' });
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const product: IProduct = await Product.create({
|
const product: IProduct = await Product.create({
|
||||||
|
@ -31,9 +25,10 @@ export async function createProduct(req: Request, res: Response) {
|
||||||
export async function listProducts(req: Request, res: Response) {
|
export async function listProducts(req: Request, res: Response) {
|
||||||
try {
|
try {
|
||||||
const { page, limit } = req.query;
|
const { page, limit } = req.query;
|
||||||
const dbPage = Number(page) || 0;
|
const products = await Product.find()
|
||||||
const dbLimit = Number(limit) || 50;
|
.sort({ price: 1 })
|
||||||
const products = await Product.find().sort({ price: 1 }).skip(Number(dbPage) * Number(dbLimit)).limit(Number(dbLimit));
|
.skip(Number(page) * Number(limit))
|
||||||
|
.limit(Number(limit));
|
||||||
|
|
||||||
res.json(products);
|
res.json(products);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
@ -41,18 +36,3 @@ 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.' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProduct(req: Request, res: Response) {
|
|
||||||
const { id } = req.params;
|
|
||||||
try {
|
|
||||||
const product = await Product.findById(id);
|
|
||||||
if(!product) {
|
|
||||||
res.status(404).json({ error: 'Product not found.' });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
res.json(product);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error getting product:', error);
|
|
||||||
res.status(500).json({ error: 'An error occurred while getting the product.' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -11,14 +11,8 @@ export async function createUser(req: Request, res: Response) {
|
||||||
if (!(email && password && firstName && lastName && address)) {
|
if (!(email && password && firstName && lastName && address)) {
|
||||||
return res.status(400).json({ error: 'All inputs are required' });
|
return res.status(400).json({ error: 'All inputs are required' });
|
||||||
}
|
}
|
||||||
// checkIfUserExists return true if the user exists
|
|
||||||
const userExists = await User.exists({ email });
|
|
||||||
if(userExists) {
|
|
||||||
return res.status(400).json({ error: 'User already exists, Try login :)' });
|
|
||||||
}
|
|
||||||
|
|
||||||
const hashedPassword = await bcrypt.hash(password, 10);
|
const hashedPassword = await bcrypt.hash(password, 10);
|
||||||
|
|
||||||
const user: IUser = await User.create({
|
const user: IUser = await User.create({
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
|
@ -27,9 +21,7 @@ export async function createUser(req: Request, res: Response) {
|
||||||
address,
|
address,
|
||||||
});
|
});
|
||||||
|
|
||||||
res.status(200).json({
|
res.json(user);
|
||||||
massage: 'User created successfully'
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating user:', error);
|
console.error('Error creating user:', error);
|
||||||
res.status(500).json({ error: 'An error occurred while creating the user.' });
|
res.status(500).json({ error: 'An error occurred while creating the user.' });
|
||||||
|
@ -62,7 +54,7 @@ export async function login(req: Request, res: Response) {
|
||||||
|
|
||||||
// Send the JWT as the response
|
// Send the JWT as the response
|
||||||
res.status(200).json({
|
res.status(200).json({
|
||||||
token
|
username: user.firstName
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during login:', error);
|
console.error('Error during login:', error);
|
||||||
|
|
|
@ -9,11 +9,12 @@ import cartRouter from './routes/cart';
|
||||||
const env = require('dotenv').config().parsed;
|
const env = require('dotenv').config().parsed;
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = env.PORT || 3000;
|
const PORT = 3000;
|
||||||
|
|
||||||
app.use(express.json());
|
app.use(express.json());
|
||||||
app.use(cookieParser())
|
app.use(cookieParser())
|
||||||
|
|
||||||
|
|
||||||
// Connect to MongoDB using Mongoose
|
// Connect to MongoDB using Mongoose
|
||||||
mongoose.connect(env.DATABASE_URL);
|
mongoose.connect(env.DATABASE_URL);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { Request, Response, NextFunction } from 'express';
|
import express, { Request, Response, NextFunction } from 'express';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt, { JwtPayload } from 'jsonwebtoken';
|
||||||
|
import cookieParser from 'cookie-parser';
|
||||||
|
|
||||||
interface AuthenticatedRequest extends Request {
|
interface AuthenticatedRequest extends Request {
|
||||||
userId?: string;
|
userId?: string;
|
||||||
|
@ -13,7 +14,7 @@ export function authenticateToken(req: AuthenticatedRequest, res: Response, next
|
||||||
return res.status(401).json({ error: 'Unauthorized' });
|
return res.status(401).json({ error: 'Unauthorized' });
|
||||||
}
|
}
|
||||||
|
|
||||||
jwt.verify(token, process.env.JWT_SECRET as string, (err: any, decoded: { userId: any; }) => {
|
const user_id = jwt.verify(token, process.env.JWT_SECRET as string, (err, decoded) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(401).json({ error: 'In Valid Token' });
|
return res.status(401).json({ error: 'In Valid Token' });
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import express from 'express';
|
import express, { Request } from 'express';
|
||||||
import { authenticateToken } from '../middlewares/checkAuth';
|
import { authenticateToken } from '../middlewares/checkAuth';
|
||||||
import { addToCart, listCart, checkout } from '../controllers/CartController';
|
import { addToCart, listCart, checkout } from '../controllers/CartController';
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
import express from 'express';
|
import express, { Request } from 'express';
|
||||||
import { authenticateToken } from '../middlewares/checkAuth';
|
import { authenticateToken } from '../middlewares/checkAuth';
|
||||||
import { createProduct, listProducts, getProduct } from '../controllers/ProductController';
|
import { createProduct, listProducts } from '../controllers/ProductController';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const productRouter = express.Router();
|
const productRouter = express.Router();
|
||||||
|
|
||||||
productRouter.post('/', authenticateToken, createProduct)
|
productRouter.post('/', authenticateToken, createProduct)
|
||||||
productRouter.get('/', listProducts);
|
productRouter.get('/', listProducts);
|
||||||
productRouter.get('/:id', getProduct);
|
|
||||||
|
|
||||||
|
|
||||||
export default productRouter;
|
export default productRouter;
|
Loading…
Reference in a new issue