diff --git a/api/compose.yml b/api/compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..091812b190d897e1d3ba8973768ea84963f3fbee --- /dev/null +++ b/api/compose.yml @@ -0,0 +1,16 @@ +version: "3.7" + +services: + database: + image: "postgres:14.3-alpine" + ports: + - 127.0.0.1:5432:5432 + environment: + - POSTGRES_USER=${POSTGRES_USER:-postgres} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-password} + - POSTGRES_DB=${POSTGRES_DB:-restaurantdb} + volumes: + - database-storage:/var/lib/postgresql/data + +volumes: + database-storage: \ No newline at end of file diff --git a/api/package-lock.json b/api/package-lock.json index e382257e812002f3f82daa5b4200058d53b6175c..fa3ecbc70c958c5f83064edaebbf7d16f52555eb 100644 --- a/api/package-lock.json +++ b/api/package-lock.json @@ -15,6 +15,7 @@ "dotenv": "^16.0.1", "express": "^4.18.1", "jsonwebtoken": "^8.5.1", + "moment": "^2.29.3", "prisma": "^3.15.1" }, "devDependencies": { @@ -1793,6 +1794,14 @@ "node": ">=10" } }, + "node_modules/moment": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==", + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -4123,6 +4132,11 @@ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, + "moment": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz", + "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==" + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", diff --git a/api/package.json b/api/package.json index 984bea3d959353c7d01f21fef59c6138d2e581a0..cdfd98a0a4c002bddd191ccee338e66d755e7d58 100644 --- a/api/package.json +++ b/api/package.json @@ -26,6 +26,7 @@ "dotenv": "^16.0.1", "express": "^4.18.1", "jsonwebtoken": "^8.5.1", + "moment": "^2.29.3", "prisma": "^3.15.1" } } diff --git a/api/prisma/migrations/20220623165636_6/migration.sql b/api/prisma/migrations/20220623165636_6/migration.sql new file mode 100644 index 0000000000000000000000000000000000000000..4380327e95645044f2761f165e44cfaa8a020049 --- /dev/null +++ b/api/prisma/migrations/20220623165636_6/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Order" ALTER COLUMN "open" SET DEFAULT true; diff --git a/api/prisma/schema.prisma b/api/prisma/schema.prisma index 39812f7d3711e5206a0fa0dcf481456fd1604fc5..ccb86666f203c50cf443f088c50a486a04a155d7 100644 --- a/api/prisma/schema.prisma +++ b/api/prisma/schema.prisma @@ -40,7 +40,7 @@ model Adress{ model Order{ id Int @id @default(autoincrement()) deliveryTime DateTime - open Boolean @default(false) + open Boolean @default(true) createdAt DateTime @default(now()) updatedAt DateTime @updatedAt quantity Int @default(1) @@ -53,7 +53,7 @@ model Order{ model Meal{ id Int @id @default(autoincrement()) - name String + name String description String? price Int createdAt DateTime @default(now()) @@ -65,7 +65,7 @@ model Meal{ model Menu{ id Int @id @default(autoincrement()) - date DateTime + date DateTime createdAt DateTime @default(now()) updatedAt DateTime @updatedAt deletedAt DateTime? diff --git a/api/src/controllers/adresses.controllers.ts b/api/src/controllers/adresses.controllers.ts index c0797f73a3b3f07c28c2210d431add96625a33b0..7dffeca9fd7635e473f5b3ce20b202efeec99707 100644 --- a/api/src/controllers/adresses.controllers.ts +++ b/api/src/controllers/adresses.controllers.ts @@ -24,7 +24,6 @@ export const getAdress = async (req: Request, res:Response) => { const adress = await prisma.adress.findFirst({ where:{ userId: req.userInfo.id, - id: parseInt(req.params.id) } }); return res.status(200).json({ @@ -39,15 +38,13 @@ export const updateAdress = async (req: Request, res:Response) => { const adress = await prisma.adress.update({ where:{ - id: parseInt(req.params.id), userId }, data:{ city, street, streetNumber, - zip, - userId + zip } }); return res.status(201).json({ @@ -61,7 +58,6 @@ export const deleteAdress = async (req: Request, res:Response) => { const adress = await prisma.adress.update({ where:{ - id: parseInt(req.params.id), userId }, data:{ diff --git a/api/src/controllers/menus.controllers.ts b/api/src/controllers/menus.controllers.ts index 3213642a6c8b7680d468ec02a5a25ed96435578b..c8d32f0f22bf9aeec27c832e00c3144a50ebc243 100644 --- a/api/src/controllers/menus.controllers.ts +++ b/api/src/controllers/menus.controllers.ts @@ -2,11 +2,11 @@ import prisma from '../client'; import { Request, Response } from 'express'; export const createMenu = async (req: Request, res: Response) => { - const date = req.body + const date = req.body.date const menu = await prisma.menu.create({ data:{ - date + date: new Date(date) } }); return res.status(201).json({ @@ -19,6 +19,9 @@ export const getMenuById = async (req: Request, res: Response) => { const menu = await prisma.menu.findUnique({ where:{ id: parseInt(req.params.id) + }, + include:{ + meals: true } }); return res.status(200).json({ @@ -27,16 +30,42 @@ export const getMenuById = async (req: Request, res: Response) => { }); }; -export const updateMenu = async (req: Request, res: Response) => { - const { date, meals } = req.body +export const addMealToMenu = async (req: Request, res: Response) => { + const mealId = req.body.id const menu = await prisma.menu.update({ where:{ id: parseInt(req.params.id) }, data:{ - date, - meals + meals: { + connect: { id: mealId } + } + }, + include:{ + meals: true + } + }); + return res.status(200).json({ + status: 'success', + data: menu + }) +}; + +export const removeMealFromMenu = async (req: Request, res: Response) => { + const mealId = req.body.id + + const menu = await prisma.menu.update({ + where:{ + id: parseInt(req.params.id) + }, + data:{ + meals: { + disconnect: { id: mealId } + } + }, + include:{ + meals: true } }); return res.status(200).json({ @@ -58,5 +87,34 @@ export const deleteMenu = async (req: Request, res: Response) => { status: 'success', data: menu }) +}; + +export const getMenus = async (req: Request, res: Response) => { + if(!req.query.date){ + const menus = await prisma.menu.findMany({ + include:{ + meals: true + } + }); + return res.status(200).json({ + status: "success", + data: menus + }) + } + else{ + const date = new Date(`${req.query.date as string} UTC`); + const menus = await prisma.menu.findMany({ + where:{ + date, + }, + include:{ + meals:true + } + }); + return res.status(200).json({ + status: "success", + data: menus + }) + } } diff --git a/api/src/controllers/orders.controllers.ts b/api/src/controllers/orders.controllers.ts index 76250eec3c73f4e7151c0f4eccd3e1163731c2aa..ad50a30829172edda6c72e7a4983aa53e0849dc8 100644 --- a/api/src/controllers/orders.controllers.ts +++ b/api/src/controllers/orders.controllers.ts @@ -8,12 +8,6 @@ export const getOrders = async (req: Request, res: Response) => { userId: id, } }); - if(!orders){ - return res.status(400).json({ - status: 'error', - message: 'there are no orders' - }); - } return res.status(200).json({ status: 'success', data: orders @@ -27,12 +21,6 @@ export const getOpenOrders = async (req: Request, res: Response) => { open: true } }) - if(!openOrders){ - return res.status(400).json({ - status: 'error', - message: 'there are no open orders' - }); - } return res.status(200).json({ status: 'success', data: openOrders @@ -40,12 +28,13 @@ export const getOpenOrders = async (req: Request, res: Response) => { } export const createOrder = async (req: Request, res: Response) => { - const { deliveryTime, mealId } = req.body; + const { deliveryTime, mealId, quantity } = req.body; const order = await prisma.order.create({ data:{ userId: req.userInfo.id, deliveryTime: new Date(deliveryTime), - mealId + mealId, + quantity } }); return res.status(201).json({ diff --git a/api/src/controllers/staff.controllers.ts b/api/src/controllers/staff.controllers.ts index ad2badbd02ae3db8e8722f12931441e835947909..644b0d053e4979dae0f656e4ff394d0a38d8d31a 100644 --- a/api/src/controllers/staff.controllers.ts +++ b/api/src/controllers/staff.controllers.ts @@ -13,7 +13,7 @@ export const login = async (req: Request, res: Response) => { } }); if(await bcrypt.compare(password, staff!.password)){ - const accessToken = jwt.sign(staff, process.env.ACCESS_TOKEN_SECRET) + const accessToken = jwt.sign(staff, process.env.ACCESS_TOKEN_SECRET_2) return res.status(200).json({ status: 'success', data: { accessToken: accessToken } @@ -29,14 +29,35 @@ export const login = async (req: Request, res: Response) => { export const addAccount = async (req: Request, res: Response) => { const { username, password } = req.body; - const account = await prisma.staff.create({ + + bcrypt.hash(password, 10, async (err: any, hash: any) => { + if(err){ + return res.status(500).json({ + status: 'error', + message: err + }); + } + const staff = await prisma.staff.create({ data:{ - username, - password + username, + password: hash } - }) - return res.status(201).json({ + }); + return res.status(201).json({ status: 'success', - data: account - }) -} \ No newline at end of file + data: staff + }); + }); +}; + +export const deleteAccount = async (req: Request, res: Response) => { + const user = await prisma.staff.delete({ + where:{ + id: parseInt(req.params.id) + } + }); + return res.status(200).json({ + status: "success", + message: "deleted" + }) +}; diff --git a/api/src/controllers/users.controllers.ts b/api/src/controllers/users.controllers.ts index 6e6a445f282be55d204a1b3011a3c071ffd09843..b390cbf6b7b8fbcfaf3988c74a33ca08e9ebadfa 100644 --- a/api/src/controllers/users.controllers.ts +++ b/api/src/controllers/users.controllers.ts @@ -54,19 +54,54 @@ export const login = async (req: Request, res: Response) => { export const deleteUser = async (req: Request, res: Response) => { const user = await prisma.user.update({ where:{ - id: parseInt(req.params.id), + id: req.userInfo.id, }, data:{ deletedAt: new Date(), } }); + return res.status(200).json({ status: 'success', - data: user + data: { + user: user, + message: "account deleted" + } }); }; -export const getUserInfo = async (req: Request, res: Response) => { +export const changePassword = async (req: Request, res: Response) => { + const userId = req.userInfo.id; + const newPassword = req.body.newPassword; + + const user = await prisma.user.findUnique({ + where:{ + id: userId + } + }); + bcrypt.hash(newPassword, 10, async (err: any, hash: any) => { + if(err){ + return res.status(500).json({ + status: 'error', + message: err + }); + } + const user = await prisma.user.update({ + where:{ + id: userId + }, + data:{ + password: hash + } + }); + return res.status(200).json({ + status: 'success', + message: "password changed" + }); + }); +}; + +export const getUserInfoById = async (req: Request, res: Response) => { const userId = parseInt(req.params.id); const user = await prisma.user.findUnique({ @@ -80,19 +115,42 @@ export const getUserInfo = async (req: Request, res: Response) => { }); }; +export const getCurrentUserInfo = async (req: Request, res: Response) => { + const userId = req.userInfo.id; + + const user = await prisma.user.findUnique({ + where:{ + id: userId + } + }); + return res.status(200).json({ + status: 'success', + data: user + }); +}; + export const updateUserInfo = async (req: Request, res: Response) => { - const { phoneNumber } = req.body; + const { email, phoneNumber, firstName, lastName } = req.body; const user = await prisma.user.update({ where:{ - id: parseInt(req.params.id) + id: req.userInfo.id }, data:{ - phoneNumber + email, + phoneNumber, + firstName, + lastName } }); + + const newAccessToken = jwt.sign(user, process.env.ACCESS_TOKEN_SECRET) + return res.status(200).json({ status: 'success', - data: user + data: { + user: user, + accessToken: newAccessToken + } }) }; \ No newline at end of file diff --git a/api/src/middleware/adresses.middleware.ts b/api/src/middleware/adresses.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..28b76c972066b8384e4e06667d61d82ba61062ea --- /dev/null +++ b/api/src/middleware/adresses.middleware.ts @@ -0,0 +1,44 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +export const validateAdressInfo = (req: Request, res: Response, next: NextFunction) => { + const { zip, street, streetNumber, city } = req.body; + + if(!zip || !street || !streetNumber || !city){ + res.status(400).json({ + status: 'error', + message: 'Missing information' + }); + } + next(); +}; + +export const ensureAdressExists = async (req: Request, res: Response, next: NextFunction) => { + const adress = await prisma.adress.findFirst({ + where:{ + userId: req.userInfo.id + } + }); + if(!adress){ + return res.status(400).json({ + status: 'error', + message: 'user does not have an adress' + }); + } + next(); +}; + +export const ensureAdressDoesNotExist = async (req: Request, res: Response, next: NextFunction) => { + const adress = await prisma.adress.findFirst({ + where:{ + userId: req.userInfo.id + } + }); + if(adress){ + return res.status(400).json({ + status: 'error', + message: 'user already has adress' + }); + } + next(); +}; \ No newline at end of file diff --git a/api/src/middleware/authorizeUser.middleware.ts b/api/src/middleware/authorizeUser.middleware.ts deleted file mode 100644 index 95984e6dba44a18d8f77b6ecb672ae2a4a089ec4..0000000000000000000000000000000000000000 --- a/api/src/middleware/authorizeUser.middleware.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { Request, Response, NextFunction } from 'express'; - -const jwt = require('jsonwebtoken'); -require('dotenv').config(); - -const authorizeUser = (req: Request, res: Response, next: NextFunction) => { - const authHeader = req.headers['authorization']; - const token = authHeader && authHeader.split(' ')[1]; - if(token == null){ - res.status(401).json({ - status: 'error', - message: 'Missing token' - }); - } - jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err: any, user: any) => { - if(err){ - res.status(403).json({ - status: 'error', - message: "Forbidden" - }) - } - req.userInfo = user; - req.staffInfo = user; - next(); - }); -} - -export default authorizeUser; \ No newline at end of file diff --git a/api/src/middleware/checkOrderExistence.middleware.ts b/api/src/middleware/checkOrderExistence.middleware.ts deleted file mode 100644 index 2fa213443b66676867887b5eada38e6373a1333d..0000000000000000000000000000000000000000 --- a/api/src/middleware/checkOrderExistence.middleware.ts +++ /dev/null @@ -1,16 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -export const checkExsitence = async (req: Request, res:Response, next: NextFunction) => { - const order = await prisma.order.findUnique({ - where:{ - id: parseInt(req.params.id) - } - }); - if(!order){ - return res.status(400).json({ - status: 'error', - message: 'order not found' - }); - } -}; diff --git a/api/src/middleware/meals.middleware.ts b/api/src/middleware/meals.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..f9624b4be52d2ddacb86598257089e45c010347e --- /dev/null +++ b/api/src/middleware/meals.middleware.ts @@ -0,0 +1,57 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +export const validateMealInfo = (req: Request, res: Response, next: NextFunction) => { + const { price } = req.body + + if(price && !(typeof(price) === 'number')){ + return res.status(400).json({ + status: 'error', + message: 'invalid price' + }); + } + next(); +}; + +export const ensureMealExist = async (req: Request, res: Response, next: NextFunction) => { + const meal = await prisma.meal.findFirst({ + where: { + AND: [ + { + id: parseInt(req.params.id), + deletedAt: null + } + ] + } + }); + if(!meal){ + return res.status(400).json({ + status: 'error', + message: 'meal not found' + }); + } + next(); +}; + +export const resolveMealInfo = async (req: Request, res: Response, next: NextFunction) => { + const meal = await prisma.meal.findFirst({ + where: { + AND: [ + { + id: parseInt(req.params.id), + deletedAt: null + } + ] + } + }); + if(!req.body.name){ + req.body.name = meal!.name; + } + if(!req.body.description){ + req.body.description = meal!.description; + } + if(!req.body.price){ + req.body.price = meal!.price; + } + next(); +}; \ No newline at end of file diff --git a/api/src/middleware/menus.middleware.ts b/api/src/middleware/menus.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..dade505f081993f3f88aa420995865472dc988f4 --- /dev/null +++ b/api/src/middleware/menus.middleware.ts @@ -0,0 +1,63 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +const moment = require('moment'); + +export const validateMenuInfo = async (req: Request, res: Response, next: NextFunction) => { + const dateString = req.body.date + if(!dateString){ + return res.status(400).json({ + status: 'error', + message: 'missing information' + }); + } + if(!moment(dateString, moment.ISO_8601, true).isValid()){ + return res.status(400).json({ + status: 'error', + message: 'invalid date' + }); + } + const menu = await prisma.menu.findFirst({ + where:{ + date: new Date(dateString) + } + }); + if(menu){ + return res.status(400).json({ + status: 'error', + message: 'menu for the day already exists' + }); + } + next(); +}; + +export const checkIfMealCanConnect = async (req: Request, res: Response, next: NextFunction) => { + const mealId = req.body.id; + const meal = await prisma.meal.findUnique({ + where:{ + id: mealId + } + }); + if(!meal){ + return res.status(400).json({ + status: "error", + message: "wrong meal id" + }) + } + next(); +}; + +export const ensureMenuExists = async (req: Request, res: Response, next: NextFunction) => { + const menu = await prisma.menu.findUnique({ + where:{ + id: parseInt(req.params.id) + } + }); + if(!menu){ + res.status(400).json({ + status: 'error', + message: 'menu not found' + }) + } + next(); +}; \ No newline at end of file diff --git a/api/src/middleware/orders.middleware.ts b/api/src/middleware/orders.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..a4093b7fc3f17a17ac4c259fff96231595e5ba4f --- /dev/null +++ b/api/src/middleware/orders.middleware.ts @@ -0,0 +1,71 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +const moment = require("moment"); + +export const checkOrderExsitence = async (req: Request, res:Response, next: NextFunction) => { + const order = await prisma.order.findFirst({ + where:{ + id: parseInt(req.params.id), + userId: req.userInfo.id + } + }); + if(!order){ + return res.status(400).json({ + status: 'error', + message: 'order not found' + }); + } + next(); +}; + +export const validateOrderCreation = async (req: Request, res: Response, next: NextFunction) => { + const { deliveryTime, mealId, quantity } = req.body; + + if(!deliveryTime || !mealId){ + return res.status(400).json({ + status: 'error', + message: 'missing information', + }); + } + if(!moment(deliveryTime, moment.ISO_8601, true).isValid() || moment(deliveryTime, moment.ISO_8601, true).isSameOrBefore(moment(new Date()).add(1, 'days'))){ + return res.status(400).json({ + status: 'error', + message: 'too late to make an order' + }); + } + const meal = await prisma.meal.findUnique({ + where:{ + id: mealId + } + }) + if(!meal){ + return res.status(400).json({ + status: 'error', + message: 'meal does not exist', + }); + } + if(quantity && !(typeof(quantity) === 'number')){ + return res.status(400).json({ + status: 'error', + message: 'invalid quantity' + }); + } + next(); +}; + +export const authorizeClosure = async (req: Request, res: Response, next: NextFunction) => { + const order = await prisma.order.findFirst({ + where:{ + id: parseInt(req.params.id), + userId: req.userInfo.id + } + }); + if(!order){ + return res.status(400).json({ + status: 'error', + message: "forbidden" + }); + } + next(); +}; \ No newline at end of file diff --git a/api/src/middleware/staff.middleware.ts b/api/src/middleware/staff.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..a296023f13ae948a836d7df43f60b885ff309690 --- /dev/null +++ b/api/src/middleware/staff.middleware.ts @@ -0,0 +1,48 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +const jwt = require('jsonwebtoken'); +require('dotenv').config(); + +export const authorizeStaff = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + if(token == null){ + return res.status(401).json({ + status: 'error', + message: 'Missing token' + }); + } + jwt.verify(token, process.env.ACCESS_TOKEN_SECRET_2, (err: any, user: any) => { + if(err){ + return res.status(403).json({ + status: 'error', + message: "Forbidden" + }) + } + req.staffInfo = user; + next(); + }); +}; + +export const validateStaffLogin = async (req: Request, res: Response, next: NextFunction) => { + const { username, password } = req.body; + if(!username || !password){ + return res.status(400).json({ + status: 'error', + message: 'Missing information' + }); + } + const staff = await prisma.staff.findUnique({ + where:{ + username, + } + }) + if(!staff){ + return res.status(400).json({ + status: 'error', + message: 'username does not exist' + }); + } + next(); +}; diff --git a/api/src/middleware/users.middleware.ts b/api/src/middleware/users.middleware.ts new file mode 100644 index 0000000000000000000000000000000000000000..99bad7ed34f33597983dfc9fc45a6cc89f1daf56 --- /dev/null +++ b/api/src/middleware/users.middleware.ts @@ -0,0 +1,160 @@ +import prisma from '../client'; +import { Request, Response, NextFunction } from 'express'; + +const jwt = require('jsonwebtoken'); +const bcrypt = require('bcrypt'); +require('dotenv').config(); + +export const authorizeUser = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers['authorization']; + const token = authHeader && authHeader.split(' ')[1]; + if(token == null){ + return res.status(401).json({ + status: 'error', + message: 'Missing token' + }); + } + jwt.verify(token, process.env.ACCESS_TOKEN_SECRET, (err: any, user: any) => { + if(err){ + return res.status(403).json({ + status: 'error', + message: "Forbidden" + }) + } + req.userInfo = user; + }); + next(); +}; + +const validateEmail = (email: string) => { + const emailRe = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + return emailRe.test(email) +} + +export const validateRegistration = async (req: Request, res: Response, next: NextFunction) => { + const { firstName, lastName, email, password, repeatPassword } = req.body; + + if(!firstName || !lastName || !email || !password || !repeatPassword){ + return res.status(400).json({ + status: 'error', + message: "Missing information" + }); + } + if(!validateEmail(email)){ + return res.status(400).json({ + status: 'error', + message: "Invalid email" + }); + } + const users = await prisma.user.findMany({ + where: { + email, + }, + }) + if(users.length > 0){ + return res.status(400).json({ + status: 'error', + message: "Email already exists" + }); + } + if(password.length < 8){ + return res.status(400).json({ + status: 'error', + message: "Password is too short" + }); + } + if(password !== repeatPassword){ + return res.status(400).json({ + status: 'error', + message: 'Passwords do not match' + }); + } + next(); +}; + +export const validateUserLogin = async (req: Request, res: Response, next: NextFunction) => { + const { email, password } = req.body; + if(!email || !password){ + return res.status(400).json({ + status: 'error', + message: 'Missing information' + }); + } + const user = await prisma.user.findUnique({ + where:{ + email, + } + }) + if(!user){ + return res.status(400).json({ + status: 'error', + message: 'email does not exist' + }); + } + next(); +}; + +const validatePhoneNumber = (phoneNumber: string) => { + const re = /^\+[0-9]{12,12}$/; + return re.test(phoneNumber); +} + +export const validateUserInfo = async (req: Request, res: Response, next: NextFunction) => { + const userId = req.userInfo.id; + const user = await prisma.user.findUnique({ + where:{ + id: userId + } + }) + if(req.body.phoneNumber && !validatePhoneNumber(req.body.phoneNumber)){ + return res.status(400).json({ + status: 'error', + message: 'invalid phone number' + }); + } + if(req.body.email && !validateEmail(req.body.email)){ + return res.status(400).json({ + status: 'error', + message: 'invalid email' + }); + } + next(); +}; + +export const validateChangePasswordInfo = async (req: Request, res: Response, next: NextFunction) => { + const { oldPassword, newPassword, repeatPassword } = req.body; + const userId = req.userInfo.id; + + if(!oldPassword || !newPassword || !repeatPassword){ + return res.status(400).json({ + status: 'error', + message: "Missing information" + }); + } + + const user = await prisma.user.findUnique({ + where:{ + id: userId + } + }); + + if(!await bcrypt.compare(oldPassword, user!.password)){ + return res.status(400).json({ + status: 'error', + message: "Wrong password" + }); + } + if(newPassword.length < 8){ + return res.status(400).json({ + status: 'error', + message: "Password is too short" + }); + } + if(newPassword !== repeatPassword){ + return res.status(400).json({ + status: 'error', + message: "Passwords do not match" + }); + } + next(); +}; \ No newline at end of file diff --git a/api/src/middleware/validateAdress.middleware.ts b/api/src/middleware/validateAdress.middleware.ts deleted file mode 100644 index 73daf5b578e89d18e5496855929b5d3a31768ff9..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateAdress.middleware.ts +++ /dev/null @@ -1,30 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; -import { rmSync } from 'fs'; - -export const validateAdressInfo = (req: Request, res: Response, next: NextFunction) => { - const { zip, street, streetNumber, city } = req.body; - - if(!zip || !street || !streetNumber || !city){ - res.status(400).json({ - status: 'error', - message: 'Missing information' - }); - } - next(); -}; - -export const ensureAdressExists = (req: Request, res: Response, next: NextFunction) => { - const adress = prisma.adress.findUnique({ - where:{ - id: parseInt(req.params.id), - userId: req.userInfo.id - } - }); - if(!adress){ - res.status(400).json({ - status: 'error', - message: 'adress not found' - }); - } -}; \ No newline at end of file diff --git a/api/src/middleware/validateLogin.middleware.ts b/api/src/middleware/validateLogin.middleware.ts deleted file mode 100644 index c288e734a28a0a600a49e41afa1117ba71a20d07..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateLogin.middleware.ts +++ /dev/null @@ -1,46 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -export const validateLogin = async (req: Request, res: Response, next: NextFunction) => { - const { email, password } = req.body; - if(!email || !password){ - res.status(400).json({ - status: 'error', - message: 'Missing information' - }); - } - const user = await prisma.user.findUnique({ - where:{ - email, - } - }) - if(!user){ - res.status(400).json({ - status: 'error', - message: 'email does not exist' - }); - } - next(); -} - -export const validateStaffLogin = async (req: Request, res: Response, next: NextFunction) => { - const { username, password } = req.body; - if(!username || !password){ - res.status(400).json({ - status: 'error', - message: 'Missing information' - }); - } - const staff = await prisma.staff.findUnique({ - where:{ - username, - } - }) - if(!staff){ - res.status(400).json({ - status: 'error', - message: 'username does not exist' - }); - } - next(); -} \ No newline at end of file diff --git a/api/src/middleware/validateMeal.middleware.ts b/api/src/middleware/validateMeal.middleware.ts deleted file mode 100644 index f2d89af57c40b92c965955fec27064c8c753df91..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateMeal.middleware.ts +++ /dev/null @@ -1,29 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -export const validateMealInfo = (req: Request, res: Response, next: NextFunction) => { - const { name, description, price } = req.body - - if(!name || !description || !price){ - return res.status(400).json({ - status: 'error', - message: 'missing information' - }); - } - next(); -}; - -export const ensureMealExist = async (req: Request, res: Response, next: NextFunction) => { - const meal = await prisma.meal.findUnique({ - where: { - id: parseInt(req.params.id) - } - }); - if(!meal){ - return res.status(400).json({ - status: 'error', - message: 'meal not found' - }); - } - next(); -}; \ No newline at end of file diff --git a/api/src/middleware/validateMenu.middleware.ts b/api/src/middleware/validateMenu.middleware.ts deleted file mode 100644 index 76a087cb69b606423f377da86836ce10a9d95f02..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateMenu.middleware.ts +++ /dev/null @@ -1,27 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -export const validateMenuInfo = (req: Request, res: Response, next: NextFunction) => { - const date = req.body.date - if(!date){ - return res.status(400).json({ - status: 'error', - message: 'missing information' - }); - } - next(); -} - -export const ensureMenuExists = async (req: Request, res: Response, next: NextFunction) => { - const menu = prisma.menu.findUnique({ - where:{ - id: parseInt(req.params.id) - } - }); - if(!menu){ - res.status(400).json({ - status: 'error', - message: 'menu not found' - }) - } -} \ No newline at end of file diff --git a/api/src/middleware/validateOrderInput.middleware.ts b/api/src/middleware/validateOrderInput.middleware.ts deleted file mode 100644 index b6b514fcbb40aaa5d583c0c1ed7b88f4942d6a3c..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateOrderInput.middleware.ts +++ /dev/null @@ -1,25 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -export const validateOrderCreation = async (req: Request, res: Response, next: NextFunction) => { - const { deliveryTime, mealId } = req.body; - - if(!deliveryTime || !mealId){ - return res.status(400).json({ - status: 'error', - message: 'missing information', - }); - } - const meal = await prisma.meal.findUnique({ - where:{ - id: mealId - } - }) - if(!meal){ - return res.status(400).json({ - status: 'error', - message: 'meal does not exist', - }); - } - -} \ No newline at end of file diff --git a/api/src/middleware/validateRegistration.middleware.ts b/api/src/middleware/validateRegistration.middleware.ts deleted file mode 100644 index acebb25b78dadece3041ec1f2feceefca18d66b8..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateRegistration.middleware.ts +++ /dev/null @@ -1,51 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - - -const validateEmail = (email: string) => { - const emailRe = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - return emailRe.test(email) -} - -const validateRegistration = async (req: Request, res: Response, next: NextFunction) => { - const { firstName, lastName, email, password, repeatPassword } = req.body; - - if(!firstName || !lastName || !email || !password){ - return res.status(400).json({ - status: 'error', - message: "Missing information" - }) - } - if(!validateEmail(email)){ - return res.status(400).json({ - status: 'error', - message: "Invalid email" - }) - } - const users = await prisma.user.findMany({ - where: { - email, - }, - }) - if(users.length > 0){ - return res.status(400).json({ - status: 'error', - message: "Email already exists" - }) - } - if(password.length < 6){ - return res.status(400).json({ - status: 'error', - message: "Password is too short" - }) - } - if(password !== repeatPassword){ - return res.status(400).json({ - status: 'error', - message: 'Passwords do not match' - }) - } - next(); -} - -export default validateRegistration; \ No newline at end of file diff --git a/api/src/middleware/validateUser.middleware.ts b/api/src/middleware/validateUser.middleware.ts deleted file mode 100644 index 31482a1c0d19a9a1c3f4fe5930d09a8afb9ca5e3..0000000000000000000000000000000000000000 --- a/api/src/middleware/validateUser.middleware.ts +++ /dev/null @@ -1,40 +0,0 @@ -import prisma from '../client'; -import { Request, Response, NextFunction } from 'express'; - -const validatePhoneNumber = (phoneNumber: string) => { - const re = /^\+[0-9]{12,12}$/; - return re.test(phoneNumber) -} - -const validateUser = async (req: Request, res: Response, next: NextFunction) => { - const userId = parseInt(req.params.id); - const user = await prisma.user.findUnique({ - where:{ - id: userId - } - }) - if(userId !== req.userInfo.id){ - return res.status(403).json({ - status: 'error', - message: "Not authorized" - }); - } - if (!user || user.deletedAt !== null) { - return res.status(404).send({ - status: 'error', - data: {}, - message: 'User not found' - }); - } - console.log(validatePhoneNumber(req.body.phoneNumber)); - if(req.body.phoneNumber && !validatePhoneNumber(req.body.phoneNumber)){ - return res.status(400).json({ - status: 'error', - message: 'wrong phone number' - }); - } - next(); -}; - -export default validateUser; - diff --git a/api/src/models/menu.ts b/api/src/models/menu.ts new file mode 100644 index 0000000000000000000000000000000000000000..cbf3fe26ce9bc7f8e56ad76482ac3885f1d46bc0 --- /dev/null +++ b/api/src/models/menu.ts @@ -0,0 +1,4 @@ +export default interface Menu{ + id: number, + date: Date +} \ No newline at end of file diff --git a/api/src/models/user.ts b/api/src/models/user.ts index 033e44ad52e829207366dfaf244d89e84a156b4b..70a7c458f0d3e3c172a83bb6b11ed55c56e81763 100644 --- a/api/src/models/user.ts +++ b/api/src/models/user.ts @@ -3,5 +3,6 @@ export default interface User{ firstName: string, lastName: string, email: string, - password: string + password: string, + phoneNumber?: string } \ No newline at end of file diff --git a/api/src/routes/adresses.routes.ts b/api/src/routes/adresses.routes.ts index f0ec01129d36a2460ddab04fd711634db4e93a4a..d1167f315d610b24c175231f3100aa58a380a472 100644 --- a/api/src/routes/adresses.routes.ts +++ b/api/src/routes/adresses.routes.ts @@ -1,14 +1,13 @@ import express from 'express'; import { createAdress, deleteAdress, getAdress, updateAdress } from '../controllers/adresses.controllers'; -import authorizeUser from '../middleware/authorizeUser.middleware'; -import { ensureAdressExists, validateAdressInfo } from '../middleware/validateAdress.middleware'; +import { ensureAdressDoesNotExist, ensureAdressExists, validateAdressInfo } from '../middleware/adresses.middleware'; +import { authorizeUser } from '../middleware/users.middleware'; const router = express.Router(); - -router.get("/:id", authorizeUser, ensureAdressExists, getAdress) -router.post("/", authorizeUser, validateAdressInfo, createAdress); -router.put("/:id", authorizeUser, ensureAdressExists, updateAdress) -router.delete("/:id", authorizeUser, ensureAdressExists, deleteAdress) +router.get("/", authorizeUser, getAdress); +router.post("/", authorizeUser, validateAdressInfo, ensureAdressDoesNotExist, createAdress); +router.patch("/", authorizeUser, ensureAdressExists, updateAdress); +router.delete("/:", authorizeUser, ensureAdressExists, deleteAdress); export default router; \ No newline at end of file diff --git a/api/src/routes/meals.routes.ts b/api/src/routes/meals.routes.ts index ece7bd4c9fe22e44f36f666bdfa20f64fbc7869e..5e4e9567a147d9191217e21298b426b47497ee77 100644 --- a/api/src/routes/meals.routes.ts +++ b/api/src/routes/meals.routes.ts @@ -1,13 +1,13 @@ import express from 'express'; import { createMeal, deleteMeal, getMealById, updateMeal } from '../controllers/meals.controllers'; -import { ensureMealExist, validateMealInfo } from '../middleware/validateMeal.middleware'; +import { ensureMealExist, resolveMealInfo, validateMealInfo } from '../middleware/meals.middleware'; +import { authorizeStaff } from '../middleware/staff.middleware'; const router = express.Router(); - -router.get("/:id", ensureMealExist, getMealById) -router.post("/", validateMealInfo, createMeal) -router.put("/:id", ensureMealExist, validateMealInfo, updateMeal) -router.delete("/:id", ensureMealExist, deleteMeal) +router.get("/:id", ensureMealExist, getMealById); +router.post("/", authorizeStaff, validateMealInfo, createMeal); +router.patch("/:id", authorizeStaff, ensureMealExist, validateMealInfo, resolveMealInfo, updateMeal); +router.delete("/:id", authorizeStaff, ensureMealExist, deleteMeal); export default router; \ No newline at end of file diff --git a/api/src/routes/menus.routes.ts b/api/src/routes/menus.routes.ts index 948a2098cccd266791ef23011f0a3fa142481467..192eae1678b9ddeefb02a5851f461d5a377d8252 100644 --- a/api/src/routes/menus.routes.ts +++ b/api/src/routes/menus.routes.ts @@ -1,12 +1,15 @@ import express from 'express'; -import { createMenu, deleteMenu, getMenuById, updateMenu } from '../controllers/menus.controllers'; -import { ensureMenuExists, validateMenuInfo } from '../middleware/validateMenu.middleware'; +import { addMealToMenu, createMenu, deleteMenu, getMenuById, getMenus, removeMealFromMenu } from '../controllers/menus.controllers'; +import { checkIfMealCanConnect, ensureMenuExists, validateMenuInfo } from '../middleware/menus.middleware'; +import { authorizeStaff } from '../middleware/staff.middleware'; const router = express.Router(); +router.get("/", getMenus); router.get("/:id", ensureMenuExists, getMenuById); -router.post("/", validateMenuInfo, createMenu); -router.put("/:id", ensureMenuExists, updateMenu); -router.delete("/:id", ensureMenuExists, deleteMenu); +router.post("/", authorizeStaff, validateMenuInfo, createMenu); +router.patch("/:id/meals/add", authorizeStaff, ensureMenuExists, checkIfMealCanConnect, addMealToMenu); +router.patch("/:id/meals/remove", authorizeStaff, ensureMenuExists, removeMealFromMenu); +router.delete("/:id", authorizeStaff, ensureMenuExists, deleteMenu); export default router; \ No newline at end of file diff --git a/api/src/routes/orders.routes.ts b/api/src/routes/orders.routes.ts index 89e1b9b81390f556bfd87ff775855be1f2bf50a3..42e5dacd955fcb5e85e07ef5fb5fd4b74e03ec0e 100644 --- a/api/src/routes/orders.routes.ts +++ b/api/src/routes/orders.routes.ts @@ -1,14 +1,13 @@ import express from 'express'; import { closeOrder, createOrder, getOpenOrders, getOrders } from '../controllers/orders.controllers'; -import authorizeUser from '../middleware/authorizeUser.middleware'; -import { checkExsitence } from '../middleware/checkOrderExistence.middleware'; -import { validateOrderCreation } from '../middleware/validateOrderInput.middleware'; +import { authorizeClosure, checkOrderExsitence, validateOrderCreation } from '../middleware/orders.middleware'; +import { authorizeUser } from '../middleware/users.middleware'; const router = express.Router(); router.get("/", authorizeUser, getOrders); -router.get("/openOrders", authorizeUser, getOpenOrders); +router.get("/open", authorizeUser, getOpenOrders); router.post("/", authorizeUser, validateOrderCreation, createOrder); -router.patch("/:id", authorizeUser, checkExsitence, closeOrder); +router.patch("/:id/close", authorizeUser, checkOrderExsitence, authorizeClosure, closeOrder); export default router; \ No newline at end of file diff --git a/api/src/routes/staff.routes.ts b/api/src/routes/staff.routes.ts index 3398d43a3a5dc5be9c9fe3638a1465508782f6f3..f12ace747e53471624a1a82ae015979fd28df558 100644 --- a/api/src/routes/staff.routes.ts +++ b/api/src/routes/staff.routes.ts @@ -1,11 +1,13 @@ import express from 'express'; -import { validateStaffLogin } from '../middleware/validateLogin.middleware'; -import { addAccount, login } from '../controllers/staff.controllers'; +import { addAccount, deleteAccount, login } from '../controllers/staff.controllers'; +import { validateStaffLogin } from '../middleware/staff.middleware'; const router = express.Router(); router.post("/login", validateStaffLogin, login); -router.post("/add", addAccount); +//dev purpose only +router.post("/add", addAccount); +router.delete("/:id", deleteAccount); export default router; \ No newline at end of file diff --git a/api/src/routes/users.routes.ts b/api/src/routes/users.routes.ts index dd15337007c7a063720a42a991bde5c6038a4e8f..5da41b8d6426e39941f8d8707190efb098f73cd6 100644 --- a/api/src/routes/users.routes.ts +++ b/api/src/routes/users.routes.ts @@ -1,16 +1,14 @@ import express from 'express'; -import { login, register, deleteUser, getUserInfo, updateUserInfo } from '../controllers/users.controllers'; -import authorizeUser from '../middleware/authorizeUser.middleware'; -import validateRegistration from '../middleware/validateRegistration.middleware'; -import validateUser from '../middleware/validateUser.middleware'; -import { validateLogin } from '../middleware/validateLogin.middleware'; +import { login, register, deleteUser, updateUserInfo, getCurrentUserInfo, changePassword } from '../controllers/users.controllers'; +import { authorizeUser, validateChangePasswordInfo, validateRegistration, validateUserInfo, validateUserLogin } from '../middleware/users.middleware'; const router = express.Router(); +router.get("/me", authorizeUser, getCurrentUserInfo); +router.patch("/me/changePassword", authorizeUser, validateChangePasswordInfo, changePassword); router.post("/signup", validateRegistration, register); -router.post("/login", validateLogin, login); -router.delete("/:id", authorizeUser, validateUser, deleteUser); -router.get("/:id", authorizeUser, validateUser, getUserInfo); -router.patch("/:id", authorizeUser, validateUser, updateUserInfo); +router.post("/login", validateUserLogin, login); +router.delete("/", authorizeUser, deleteUser); +router.patch("/", authorizeUser, validateUserInfo, updateUserInfo); export default router; \ No newline at end of file