Compare commits
7 commits
4e1ace0586
...
e44d9f0b6f
Author | SHA1 | Date | |
---|---|---|---|
e44d9f0b6f | |||
22429a7b32 | |||
49a80d4790 | |||
327eadf8c1 | |||
47dcaabc5c | |||
84af08450e | |||
ca14c2ec4f |
12 changed files with 176 additions and 25 deletions
2
.dockerignore
Normal file
2
.dockerignore
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
npm-debug.log
|
13
Dockerfile
Normal file
13
Dockerfile
Normal file
|
@ -0,0 +1,13 @@
|
|||
FROM node:14-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY package*.json ./
|
||||
|
||||
RUN npm install --production
|
||||
|
||||
COPY . .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
CMD ["npm", "start"]
|
98
README.md
Normal file
98
README.md
Normal file
|
@ -0,0 +1,98 @@
|
|||
# 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,5 +1,16 @@
|
|||
version: '3'
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
ports:
|
||||
- 3000:3000
|
||||
volumes:
|
||||
- ./src:/app/src
|
||||
- ./dist:/app/dist
|
||||
depends_on:
|
||||
- mongodb
|
||||
mongodb:
|
||||
image: arm64v8/mongo:4.0
|
||||
restart: always
|
||||
|
@ -9,11 +20,11 @@ services:
|
|||
- structshare_vol:/data/db
|
||||
- ./init-scripts/init.js:/docker-entrypoint-initdb.d/mongo-init.js
|
||||
environment:
|
||||
- MONGO_INITDB_DATABASE=${DB_DATABASE}
|
||||
- MONGO_INITDB_ROOT_USERNAME=${DB_USERNAME}
|
||||
- MONGO_INITDB_ROOT_PASSWORD=${DB_PASSWORD}
|
||||
- MONGO_INITDB_DATABASE=your-database-name
|
||||
- MONGO_INITDB_ROOT_USERNAME=your-username
|
||||
- MONGO_INITDB_ROOT_PASSWORD=your-password
|
||||
platform: linux/arm64/v8
|
||||
expose:
|
||||
- 27017
|
||||
volumes:
|
||||
structshare_vol:
|
||||
structshare_vol:
|
||||
|
|
|
@ -5,7 +5,8 @@
|
|||
"main": "index.js",
|
||||
"scripts": {
|
||||
"dev": "nodemon dist/index.js",
|
||||
"start": "node dist/index.js"
|
||||
"start": "node dist/index.js",
|
||||
"build": "tsc -p ."
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Request, Response } from 'express';
|
||||
import { Cart, ICart, Product, Order, IOrder } from '../mongoose/Schema';
|
||||
import { Cart, ICart, Order } from '../mongoose/Schema';
|
||||
import { sendEmailasync } from '../services/sendGrid';
|
||||
import { config } from 'dotenv';
|
||||
|
||||
|
@ -55,7 +55,7 @@ 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;
|
||||
|
|
|
@ -5,7 +5,13 @@ export async function createProduct(req: Request, res: Response) {
|
|||
try {
|
||||
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.' });
|
||||
res.status(400).json({ error: 'Name, description, price are required.' });
|
||||
return;
|
||||
}
|
||||
|
||||
const productExists = await Product.exists({ name, userId });
|
||||
if(productExists) {
|
||||
res.status(400).json({ error: 'Product already exists.' });
|
||||
return;
|
||||
}
|
||||
const product: IProduct = await Product.create({
|
||||
|
@ -25,10 +31,9 @@ export async function createProduct(req: Request, res: Response) {
|
|||
export async function listProducts(req: Request, res: Response) {
|
||||
try {
|
||||
const { page, limit } = req.query;
|
||||
const products = await Product.find()
|
||||
.sort({ price: 1 })
|
||||
.skip(Number(page) * Number(limit))
|
||||
.limit(Number(limit));
|
||||
const dbPage = Number(page) || 0;
|
||||
const dbLimit = Number(limit) || 50;
|
||||
const products = await Product.find().sort({ price: 1 }).skip(Number(dbPage) * Number(dbLimit)).limit(Number(dbLimit));
|
||||
|
||||
res.json(products);
|
||||
} catch (error) {
|
||||
|
@ -36,3 +41,18 @@ export async function listProducts(req: Request, res: Response) {
|
|||
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,8 +11,14 @@ export async function createUser(req: Request, res: Response) {
|
|||
if (!(email && password && firstName && lastName && address)) {
|
||||
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 user: IUser = await User.create({
|
||||
firstName,
|
||||
lastName,
|
||||
|
@ -21,7 +27,9 @@ export async function createUser(req: Request, res: Response) {
|
|||
address,
|
||||
});
|
||||
|
||||
res.json(user);
|
||||
res.status(200).json({
|
||||
massage: 'User created successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error creating user:', error);
|
||||
res.status(500).json({ error: 'An error occurred while creating the user.' });
|
||||
|
@ -54,7 +62,7 @@ export async function login(req: Request, res: Response) {
|
|||
|
||||
// Send the JWT as the response
|
||||
res.status(200).json({
|
||||
username: user.firstName
|
||||
token
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error during login:', error);
|
||||
|
|
|
@ -9,12 +9,11 @@ import cartRouter from './routes/cart';
|
|||
const env = require('dotenv').config().parsed;
|
||||
|
||||
const app = express();
|
||||
const PORT = 3000;
|
||||
const PORT = env.PORT || 3000;
|
||||
|
||||
app.use(express.json());
|
||||
app.use(cookieParser())
|
||||
|
||||
|
||||
// Connect to MongoDB using Mongoose
|
||||
mongoose.connect(env.DATABASE_URL);
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import jwt, { JwtPayload } from 'jsonwebtoken';
|
||||
import cookieParser from 'cookie-parser';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
interface AuthenticatedRequest extends Request {
|
||||
userId?: string;
|
||||
|
@ -14,7 +13,7 @@ export function authenticateToken(req: AuthenticatedRequest, res: Response, next
|
|||
return res.status(401).json({ error: 'Unauthorized' });
|
||||
}
|
||||
|
||||
const user_id = jwt.verify(token, process.env.JWT_SECRET as string, (err, decoded) => {
|
||||
jwt.verify(token, process.env.JWT_SECRET as string, (err: any, decoded: { userId: any; }) => {
|
||||
if (err) {
|
||||
return res.status(401).json({ error: 'In Valid Token' });
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import express, { Request } from 'express';
|
||||
import express from 'express';
|
||||
import { authenticateToken } from '../middlewares/checkAuth';
|
||||
import { addToCart, listCart, checkout } from '../controllers/CartController';
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import express, { Request } from 'express';
|
||||
import express from 'express';
|
||||
import { authenticateToken } from '../middlewares/checkAuth';
|
||||
import { createProduct, listProducts } from '../controllers/ProductController';
|
||||
|
||||
|
||||
import { createProduct, listProducts, getProduct } from '../controllers/ProductController';
|
||||
|
||||
const productRouter = express.Router();
|
||||
|
||||
productRouter.post('/', authenticateToken, createProduct)
|
||||
productRouter.get('/', listProducts);
|
||||
productRouter.get('/:id', getProduct);
|
||||
|
||||
|
||||
export default productRouter;
|
Loading…
Reference in a new issue