Compare commits

..

No commits in common. "e44d9f0b6f7fb353f87ddf04b3fe6903280cc08b" and "4e1ace0586ded60fae7372c6b4339edca3a00286" have entirely different histories.

12 changed files with 25 additions and 176 deletions

View file

@ -1,2 +0,0 @@
node_modules
npm-debug.log

View file

@ -1,13 +0,0 @@
FROM node:14-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --production
COPY . .
EXPOSE 3000
CMD ["npm", "start"]

View file

@ -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"
}
```

View file

@ -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:

View file

@ -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",

View file

@ -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;

View file

@ -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.' });
}
}

View file

@ -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);

View file

@ -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);

View file

@ -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' });
} }

View file

@ -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';

View file

@ -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;