diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6c0bedb --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +client +node_modules +requests +mongodb +src \ No newline at end of file diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..59bb440 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +MONGODB_URL= +PORT= +JWT_SECRET= +CLOUDINARY_CLOUD_NAME= +CLOUDINARY_API_KEY= +CLOUDINARY_API_SECRET= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 211cc06..a54ac4f 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ node_modules .env ## upload -uploads \ No newline at end of file +uploads + +dist \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8030940 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM node:16-alpine + +WORKDIR /app + +COPY package*.json . + +RUN npm ci + +COPY . . + +ENV NODE_ENV=production + +CMD ["npm", "start"] \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..47652db --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3' + +services: + mongo: + image: mongo + ports: + - 27017:27017 + restart: always + environment: + MONGO_INITDB_ROOT_USERNAME: admin + MONGO_INITDB_ROOT_PASSWORD: admin + api: + build: . + restart: always + ports: + - 4000:8080 + env_file: .env diff --git a/package-lock.json b/package-lock.json index a52c764..942da36 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "FaztWare", + "name": "faztware", "version": "0.0.1", "lockfileVersion": 1, "requires": true, @@ -2105,10 +2105,9 @@ "optional": true }, "core-js": { - "version": "3.16.2", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.2.tgz", - "integrity": "sha512-P0KPukO6OjMpjBtHSceAZEWlDD1M2Cpzpg6dBbrjFqFhBHe/BwhxaP820xKOjRn/lZRQirrCusIpLS/n2sgXLQ==", - "dev": true + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.17.2.tgz", + "integrity": "sha512-XkbXqhcXeMHPRk2ItS+zQYliAMilea2euoMsnpRRdDad6b2VY6CQQcwz1K8AnWesfw4p165RzY0bTnr3UrbYiA==" }, "core-js-compat": { "version": "3.16.2", @@ -4155,8 +4154,7 @@ "regenerator-runtime": { "version": "0.13.9", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz", - "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==", - "dev": true + "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA==" }, "regenerator-transform": { "version": "0.14.5", diff --git a/package.json b/package.json index df234a5..7800abf 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,23 @@ { - "name": "FaztWare", + "name": "faztware", "version": "0.0.1", "description": "An ecommerce website", "main": "index.js", + "repository": { + "url": "https://github.com/FaztWeb/faztware" + }, "scripts": { + "start": "node dist/index.js", + "build": "babel src -d dist", "dev": "nodemon src/index.js --exec babel-node --ignore client" }, "keywords": [], - "author": "", + "author": "fazttech", "license": "ISC", "dependencies": { "bcryptjs": "^2.4.3", "cloudinary": "^1.26.3", + "core-js": "^3.17.2", "cors": "^2.8.5", "dotenv": "^10.0.0", "express": "^4.17.1", @@ -20,7 +26,8 @@ "joi": "^17.4.2", "jsonwebtoken": "^8.5.1", "mongoose": "^6.0.1", - "morgan": "^1.10.0" + "morgan": "^1.10.0", + "regenerator-runtime": "^0.13.9" }, "devDependencies": { "@babel/cli": "^7.14.8", diff --git a/src/config.js b/src/config.js index d1acf0b..7982818 100644 --- a/src/config.js +++ b/src/config.js @@ -5,7 +5,7 @@ config(); export const MONGODB_URL = process.env.MONGODB_URL || ""; /* Server */ -export const PORT = process.env.PORT || 4000; +export const PORT = process.env.PORT || 8080; /* JWT */ export const JWT_SECRET = process.env.JWT_SECRET || ""; diff --git a/src/controllers/auth.controller.js b/src/controllers/auth.controller.js index be5cd4e..04230fc 100644 --- a/src/controllers/auth.controller.js +++ b/src/controllers/auth.controller.js @@ -1,6 +1,4 @@ import { User } from "../models"; -import jwt from "jsonwebtoken"; -import { JWT_SECRET } from "../config"; import { userSchema } from "../libs/schema.validator"; import createError from "http-errors"; import { signAccessToken } from "../helpers/signAccessToken"; @@ -28,14 +26,14 @@ export const login = async (req, res, next) => { export const register = async (req, res, next) => { try { - const result = await userSchema.validateAsync(req.body); + const payload = await userSchema.validateAsync(req.body); - const userFound = await User.findOne({ email: result.email }); + const userFound = await User.findOne({ email: payload.email }); if (userFound) throw createError.Conflict("The user already exists"); - const user = new User({ email: result.email, password: result.password }); - user.password = await user.generateHash(user.password); + const password = await user.generateHash(payload.password); + const user = new User({ email: payload.email, password }); const userSaved = await user.save(); diff --git a/src/controllers/product.controller.js b/src/controllers/product.controller.js index 26bb25c..77a9a34 100644 --- a/src/controllers/product.controller.js +++ b/src/controllers/product.controller.js @@ -1,41 +1,37 @@ import Product from "../models/Product"; import { uploadImage } from "../helpers/cloudinary"; import createError from "http-errors"; +import { productSchema } from "../libs/schema.validator"; -export const getProducts = async (req, res) => { - const products = await Product.find(); - res.json(products); +export const getProducts = async (req, res, next) => { + try { + const products = await Product.find(); + res.json(products); + } catch (error) { + next(error) + } }; export const createProduct = async (req, res, next) => { try { - let imageURL = ""; - const { name, price, description, quantity } = req.body; - + const payload = await productSchema.validateAsync(req.body); + const { name, price, description, quantity } = payload; const productFound = await Product.findOne({ name: name }); - if (productFound) throw createError.Conflict("Product Already exists"); - - /* TODO: validate fields */ - if (req.files && req.files.image) { - const result = await uploadImage(req.files.image.tempFilePath); - imageURL = result.secure_url; - } - + const result = await uploadImage(req.files?.image?.tempFilePath); const newProduct = new Product({ name, price, description, quantity, images: { - url: imageURL, + url: result ? result.secure_url : "", }, }); - await newProduct.save(); - res.json(newProduct); } catch (error) { + if (error.isJoi) error.status = 400; console.log(error); next(error); } @@ -45,17 +41,22 @@ export const updateProduct = (req, res) => { res.json("updating products"); }; -export const getProduct = (req, res) => { - res.json("get product"); +export const getProduct = (req, res, next) => { + try { + const { id } = req.params; + const productFound = await Product.findById(id); + if (!productFound) return res.sendStatus(404); + res.json(productFound); + } catch (error) { + next(error); + } }; export const deleteProduct = async (req, res) => { const { id } = req.params; - try { const productDeleted = await Product.findByIdAndDelete(id); if (productDeleted) return res.sendStatus(204); - return res.sendStatus(404); } catch (error) { console.log(error); diff --git a/src/controllers/users.controller.js b/src/controllers/users.controller.js index e69de29..7433894 100644 --- a/src/controllers/users.controller.js +++ b/src/controllers/users.controller.js @@ -0,0 +1,21 @@ +import User from "../models/User"; + +export const getUsers = async (req, res, next) => { + try { + const users = await User.find(); + res.json(users); + } catch (error) { + next(error) + } +}; + +export const getUser = (req, res, next) => { + try { + const { id } = req.params; + const userFound = await User.findById(id); + if (!userFound) return res.sendStatus(404); + res.json(userFound); + } catch (error) { + next(error); + } +}; \ No newline at end of file diff --git a/src/helpers/cloudinary.js b/src/helpers/cloudinary.js index 2292f13..ec5c5c3 100644 --- a/src/helpers/cloudinary.js +++ b/src/helpers/cloudinary.js @@ -13,9 +13,8 @@ cloudinary.config({ export const uploadImage = async (file) => { try { - const result = await cloudinary.uploader.upload(file); - console.log(result); - return result; + if (!file) return null; + return await cloudinary.uploader.upload(file); } catch (error) { console.error(error); } diff --git a/src/helpers/signAccessToken.js b/src/helpers/signAccessToken.js index e15a3cb..b76c536 100644 --- a/src/helpers/signAccessToken.js +++ b/src/helpers/signAccessToken.js @@ -7,7 +7,7 @@ export const signAccessToken = (userId) => { reject(new Error("userId is required")); } jwt.sign({ id: userId }, JWT_SECRET, (err, token) => { - if (err) return reject(err); + if (err) reject(err); resolve(token); }); }); diff --git a/src/index.js b/src/index.js index 2a957ef..8a6ccb2 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,6 @@ import express from "express"; +import "core-js/stable"; +import "regenerator-runtime/runtime"; import "./config/mongoose"; import cors from "cors"; import morgan from "morgan"; @@ -8,7 +10,7 @@ import fileUpload from "express-fileupload"; import productRoutes from "./routes/products.routes"; import authRoutes from "./routes/auth.routes"; -const app = express(); +export const app = express(); app.use(cors()); app.use(morgan("dev")); @@ -21,8 +23,8 @@ app.use( }) ); -app.use(productRoutes); -app.use(authRoutes); +app.use("/products", productRoutes); +app.use("/auth", authRoutes); app.use((err, req, res, next) => { res.status(err.status || 500); diff --git a/src/libs/schema.validator.js b/src/libs/schema.validator.js index 021bb8a..e5be6ab 100644 --- a/src/libs/schema.validator.js +++ b/src/libs/schema.validator.js @@ -5,4 +5,11 @@ const userSchema = Joi.object({ password: Joi.string().required().min(6).max(30), }); -export { userSchema }; +const productSchema = Joi.object({ + name: Joi.string().required(), + price: Joi.number().required(), + description: Joi.string().optional(), + quantity: Joi.number().required() +}) + +export { userSchema, productSchema }; diff --git a/src/middlewares/auth.middleware.js b/src/middlewares/auth.middleware.js index 2ca09b9..0a1b27d 100644 --- a/src/middlewares/auth.middleware.js +++ b/src/middlewares/auth.middleware.js @@ -2,7 +2,7 @@ import jwt from "jsonwebtoken"; import { JWT_SECRET } from "../config"; export const verifyToken = (req, res, next) => { - const token = req.headers.authorization; + const token = req.headers?.authorization; if (!token) return res.status(401).send({ message: "Unauthorized" }); diff --git a/src/routes/auth.routes.js b/src/routes/auth.routes.js index d4ef39a..4c0a4c2 100644 --- a/src/routes/auth.routes.js +++ b/src/routes/auth.routes.js @@ -4,10 +4,10 @@ import { verifyToken } from "../middlewares/auth.middleware"; const router = Router(); -router.post("/auth/login", login); +router.post("/login", login); -router.post("/auth/register", register); +router.post("/register", register); -router.get("/auth/profile", verifyToken, profile); +router.get("/profile", verifyToken, profile); export default router; diff --git a/src/routes/products.routes.js b/src/routes/products.routes.js index a8ade7c..949e151 100644 --- a/src/routes/products.routes.js +++ b/src/routes/products.routes.js @@ -10,14 +10,14 @@ import { verifyToken } from "../middlewares/auth.middleware"; const router = Router(); -router.get("/products", getProducts); +router.get("/", getProducts); -router.post("/products", verifyToken, createProduct); +router.post("/", verifyToken, createProduct); -router.get("/products/:id", getProduct); +router.get("/:id", getProduct); -router.put("/products/:id", verifyToken, updateProduct); +router.put("/:id", verifyToken, updateProduct); -router.delete("/products/:id", verifyToken, deleteProduct); +router.delete("/:id", verifyToken, deleteProduct); export default router; diff --git a/src/routes/users.routes.js b/src/routes/users.routes.js index 519399b..0370a7c 100644 --- a/src/routes/users.routes.js +++ b/src/routes/users.routes.js @@ -1,4 +1,14 @@ import { Router } from "express"; +import { + getUsers, + getUser, +} from "../controllers/user.controller"; +import { verifyToken } from "../middlewares/auth.middleware"; + const router = Router(); +router.get("/", verifyToken, getUsers); + +router.get("/:id", verifyToken, getUser); + export default router;