diff --git a/backend/.eslintrc.js b/backend/.eslintrc.js
new file mode 100644
index 0000000..cb78bd2
--- /dev/null
+++ b/backend/.eslintrc.js
@@ -0,0 +1,32 @@
+module.exports = {
+ 'env': {
+ 'browser': true,
+ 'es2021': true,
+ 'node': true
+ },
+ 'extends': 'eslint:recommended',
+ 'overrides': [
+ ],
+ 'parserOptions': {
+ 'ecmaVersion': 'latest',
+ 'sourceType': 'module'
+ },
+ 'rules': {
+ 'indent': [
+ 'error',
+ 'tab'
+ ],
+ 'linebreak-style': [
+ 'error',
+ 'unix'
+ ],
+ 'quotes': [
+ 'error',
+ 'single'
+ ],
+ 'semi': [
+ 'error',
+ 'always'
+ ]
+ }
+};
diff --git a/backend/app/controllers/appController.js b/backend/app/controllers/appController.js
index bdfb315..bebfa13 100644
--- a/backend/app/controllers/appController.js
+++ b/backend/app/controllers/appController.js
@@ -1,46 +1,46 @@
-const { getRestaurantFromId, getRestaurantFromName, getRestaurantFromHostname } = require('../datamodels');
+const { getRestaurantById, getRestaurantByName, getRestaurantByHostname } = require('../datamodels');
-const getRestaurantById = async (req, res) => {
- const { params: { restaurantId } } = req;
- console.log(req.params);
+const getRestaurantByIdController = async (req, res) => {
+ const { params: { restaurantId } } = req;
+ console.log(req.params);
- try {
- const result = await getRestaurantFromId(restaurantId);
- res.send(result);
- } catch (error) {
- res.sendStatus(404);
- }
+ try {
+ const result = await getRestaurantById(restaurantId);
+ res.send(result);
+ } catch (error) {
+ res.sendStatus(404);
+ }
};
-const getRestaurantByName = async (req, res) => {
- const { params: { restaurantName } } = req;
+const getRestaurantByNameController = async (req, res) => {
+ const { params: { restaurantName } } = req;
- try {
- const result = await getRestaurantFromName(restaurantName);
- res.send(result);
- } catch (error) {
- res.sendStatus(404);
- }
+ try {
+ const result = await getRestaurantByName(restaurantName);
+ res.send(result);
+ } catch (error) {
+ res.sendStatus(404);
+ }
};
-const hostnameCheck = async (req, res) => {
- const { body: { hostname } } = req;
- if (!hostname) {
- res.sendStatus(404);
- } else {
- try {
- const result = await getRestaurantFromHostname(hostname);
- if(!result) res.sendStatus(404);
- res.send(result);
- } catch (error) {
- res.sendStatus(404);
- }
- }
+const getRestaurantByHostnameController = async (req, res) => {
+ const { body: { hostname } } = req;
+ if (!hostname) {
+ res.sendStatus(404);
+ } else {
+ try {
+ const result = await getRestaurantByHostname(hostname);
+ if (!result) res.sendStatus(404);
+ res.send(result);
+ } catch (error) {
+ res.sendStatus(404);
+ }
+ }
};
module.exports = {
- getRestaurantById,
- getRestaurantByName,
- hostnameCheck,
+ getRestaurantByIdController,
+ getRestaurantByNameController,
+ getRestaurantByHostnameController,
};
diff --git a/backend/app/controllers/eventsController/db/onOrderChange.js b/backend/app/controllers/eventsController/db/onOrderChange.js
index 9b9188f..0cc152c 100644
--- a/backend/app/controllers/eventsController/db/onOrderChange.js
+++ b/backend/app/controllers/eventsController/db/onOrderChange.js
@@ -1,38 +1,43 @@
-const { Orders } = require("../../../db/models");
+const { Orders } = require('../../../db/models');
module.exports = async (mongoEvent, io) => {
- if (mongoEvent.operationType === "insert") {
- const ownerRoomName = `${mongoEvent.fullDocument.restaurantId}:owner`;
- const tabRoomName = `${mongoEvent.fullDocument.restaurantId}:${mongoEvent.fullDocument.tabId}`;
- //todo remove duplicate code from here. make a function that returns
- //the order and use it here and in controllers
- const order = await Orders.findById(mongoEvent.fullDocument._id)
- .populate('items')
- .populate({
- path: 'tabId',
- populate: {
- path: 'tableId'
- }
- }).exec();
+ if (mongoEvent.operationType === 'insert') {
+ const ownerRoomName = `${mongoEvent.fullDocument.restaurantId}:owner`;
+ const tabRoomName = `${mongoEvent.fullDocument.restaurantId}:${mongoEvent.fullDocument.tabId}`;
+ //todo remove duplicate code from here. make a function that returns
+ //the order and use it here and in controllers
+ const order = await Orders.findById(mongoEvent.fullDocument._id)
+ .populate({
+ path: 'items',
+ populate: {
+ path: 'recipe'
+ }
+ })
+ .populate({
+ path: 'tabId',
+ populate: {
+ path: 'tableId'
+ }
+ }).exec();
- io.to(ownerRoomName).emit("order:new", { order });
- console.log(`New order sent in room: "${ownerRoomName}"`);
+ io.to(ownerRoomName).emit('order:new', { order });
+ console.log(`New order sent in room: "${ownerRoomName}"`);
- io.to(tabRoomName).emit("order:new", { order });
- console.log(`New order sent in room: "${tabRoomName}"`);
- }
+ io.to(tabRoomName).emit('order:new', { order });
+ console.log(`New order sent in room: "${tabRoomName}"`);
+ }
- if (mongoEvent.operationType === "update") {
- const order = await Orders.findById(mongoEvent.documentKey._id);
- const ownerRoomName = `${order.restaurantId}:owner`;
- const tabRoomName = `${order.restaurantId}:${order.tabId}`;
+ if (mongoEvent.operationType === 'update') {
+ const order = await Orders.findById(mongoEvent.documentKey._id);
+ const ownerRoomName = `${order.restaurantId}:owner`;
+ const tabRoomName = `${order.restaurantId}:${order.tabId}`;
- io.to(ownerRoomName).emit("order:update", { id: order._id, status: order.status });
- console.log(`New order update sent in room: "${ownerRoomName}"`);
+ io.to(ownerRoomName).emit('order:update', { id: order._id, status: order.status });
+ console.log(`New order update sent in room: "${ownerRoomName}"`);
- io.to(tabRoomName).emit("order:update", { id: order._id, status: order.status });
- console.log(`New order update sent in room: "${tabRoomName}"`);
+ io.to(tabRoomName).emit('order:update', { id: order._id, status: order.status });
+ console.log(`New order update sent in room: "${tabRoomName}"`);
- }
+ }
};
\ No newline at end of file
diff --git a/backend/app/controllers/eventsController/db/onTabChange.js b/backend/app/controllers/eventsController/db/onTabChange.js
index ebdfb73..c078d25 100644
--- a/backend/app/controllers/eventsController/db/onTabChange.js
+++ b/backend/app/controllers/eventsController/db/onTabChange.js
@@ -1,48 +1,47 @@
-const { Tabs } = require("../../../db/models");
+const { Tabs } = require('../../../db/models');
const propagationDelay = 1000;
module.exports = async (mongoEvent, io) => {
- if (mongoEvent.operationType === "insert") {
- // Give some time for the document to be created and propagated
- setTimeout(() => {
- console.log(' ');
-
- const tab = mongoEvent.fullDocument;
- const ownerRoomName = `${tab.restaurantId}:owner`;
-
- io.to(ownerRoomName).emit("tab:open", { tableId: tab.tableId, tabId: tab._id });
- console.log(`[Events] [Delayed ${propagationDelay/1000}s] New tab open request sent in room: "${ownerRoomName}"`);
- }, propagationDelay);
- }
-
- if (mongoEvent.operationType === "update") {
-
- // Call waiter
- if (mongoEvent.updateDescription.updatedFields?.callWaiter === 'called') {
- console.log(' ');
- const tab = await Tabs.findById(mongoEvent.documentKey._id)
- .populate('tableId')
- .exec();
-
- const ownerRoomName = `${tab.restaurantId}:owner`;
-
- io.to(ownerRoomName).emit("waiter:notification", { tableNo: tab.tableId.tableNo, tabId: tab._id });
- console.log(`[Events] New waiter request sent in room: "${ownerRoomName}"`);
- }
-
- // Close tab
- if (mongoEvent.updateDescription.updatedFields?.status === 'closed') {
- console.log(' ');
- const tab = await Tabs.findById(mongoEvent.documentKey._id);
-
- const ownerRoomName = `${tab.restaurantId}:owner`;
- const tabRoomName = `${tab.restaurantId}:${tab._id}`;
-
- io.to(ownerRoomName).emit("tab:closed", { tableId: tab.tableId, tabId: tab._id });
- console.log(`[Events] Tab closed sent in room: "${ownerRoomName}"`);
-
- io.to(tabRoomName).emit("tab:closed", { tabId: tab._id });
- console.log(`[Events] Tab closed sent in room: "${tabRoomName}"`);
- }
- }
+ if (mongoEvent.operationType === 'insert') {
+ console.log('Tab inserted');
+ // Give some time for the document to be created and propagated
+ setTimeout(() => {
+ const tab = mongoEvent.fullDocument;
+ const ownerRoomName = `${tab.restaurantId}:owner`;
+
+ io.to(ownerRoomName).emit('tab:open', { tableId: tab.tableId, tabId: tab._id });
+ console.log(`[Events] [Delayed ${propagationDelay/1000}s] New tab open request sent in room: "${ownerRoomName}"`);
+ }, propagationDelay);
+ }
+
+ if (mongoEvent.operationType === 'update') {
+
+ // Call waiter
+ if (mongoEvent.updateDescription.updatedFields?.callWaiter === 'called') {
+ console.log(' ');
+ const tab = await Tabs.findById(mongoEvent.documentKey._id)
+ .populate('tableId')
+ .exec();
+
+ const ownerRoomName = `${tab.restaurantId}:owner`;
+
+ io.to(ownerRoomName).emit('waiter:notification', { tableNo: tab.tableId.tableNo, tabId: tab._id });
+ console.log(`[Events] New waiter request sent in room: "${ownerRoomName}"`);
+ }
+
+ // Close tab
+ if (mongoEvent.updateDescription.updatedFields?.status === 'closed') {
+ console.log(' ');
+ const tab = await Tabs.findById(mongoEvent.documentKey._id);
+
+ const ownerRoomName = `${tab.restaurantId}:owner`;
+ const tabRoomName = `${tab.restaurantId}:${tab._id}`;
+
+ io.to(ownerRoomName).emit('tab:closed', { tableId: tab.tableId, tabId: tab._id });
+ console.log(`[Events] Tab closed sent in room: "${ownerRoomName}"`);
+
+ io.to(tabRoomName).emit('tab:closed', { tabId: tab._id });
+ console.log(`[Events] Tab closed sent in room: "${tabRoomName}"`);
+ }
+ }
};
\ No newline at end of file
diff --git a/backend/app/controllers/eventsController/socket/onConnect.js b/backend/app/controllers/eventsController/socket/onConnect.js
index 8fc4e62..29562cc 100644
--- a/backend/app/controllers/eventsController/socket/onConnect.js
+++ b/backend/app/controllers/eventsController/socket/onConnect.js
@@ -1,35 +1,42 @@
-const { Restaurants } = require("../../../db/models");
+const { Restaurants } = require('../../../db/models');
module.exports = async (socket) => {
- if(socket.handshake.query.restaurantId){
- const restaurant = await Restaurants.findById(socket.handshake.query.restaurantId);
-
- //todo: implement proper authentication of owner
- if(socket.handshake.query.owner === "true") {
- console.log("Owner from restaurant " + restaurant.name + " connected!");
- socket.join(`${socket.handshake.query.restaurantId}:owner`);
- console.log(`Connected owner to room: "${socket.handshake.query.restaurantId}:owner"`);
+ if(socket.handshake.query.restaurantId){
+ let restaurant;
+ try {
+ restaurant = await Restaurants.findById(socket.handshake.query.restaurantId);
+ } catch (error) {
+ console.log(error);
+ }
+ if(restaurant){
+ //todo: implement proper authentication of owner
+ if(socket.handshake.query.owner === 'true') {
+ console.log('Owner from restaurant ' + restaurant.name + ' connected!');
+ socket.join(`${socket.handshake.query.restaurantId}:owner`);
+ console.log(`Connected owner to room: "${socket.handshake.query.restaurantId}:owner"`);
- } else if (socket.handshake.query.tabId != null){
- console.log("Client from restaurant " + restaurant?.name + " connected!");
+ } else if (socket.handshake.query.tabId != null){
+ console.log('Client from restaurant ' + restaurant?.name + ' connected!');
- socket.join(`${socket.handshake.query.restaurantId}:globalNotifications`);
- console.log(`Connected client to room (Global Notifications): "${socket.handshake.query.restaurantId}:globalNotifications"`);
+ socket.join(`${socket.handshake.query.restaurantId}:globalNotifications`);
+ console.log(`Connected client to room (Global Notifications): "${socket.handshake.query.restaurantId}:globalNotifications"`);
- socket.join(`${socket.handshake.query.restaurantId}:${socket.handshake.query.tabId}`);
- console.log(`Connected client to room (Tab): "${socket.handshake.query.restaurantId}:${socket.handshake.query.tabId}"`);
+ socket.join(`${socket.handshake.query.restaurantId}:${socket.handshake.query.tabId}`);
+ console.log(`Connected client to room (Tab): "${socket.handshake.query.restaurantId}:${socket.handshake.query.tabId}"`);
- } else {
- try {
- delete socket.handshake.query.transport;
- delete socket.handshake.query.EIO;
- delete socket.handshake.query.t;
- } catch (e) {
- console.log("Error deleting query params from socket handshake: " + e);
- }
- console.log("Disconnected client that had the following query: " + JSON.stringify(socket.handshake.query));
- socket.disconnect();
- }
- }
+ } else {
+ try {
+ delete socket.handshake.query.transport;
+ delete socket.handshake.query.EIO;
+ delete socket.handshake.query.t;
+ } catch (e) {
+ console.log('Error deleting query params from socket handshake: ' + e);
+ }
+ console.log('Disconnected client that had the following query: ' + JSON.stringify(socket.handshake.query));
+ socket.disconnect();
+ }
+ }
+
+ }
};
\ No newline at end of file
diff --git a/backend/app/controllers/eventsController/socket/onDisconnect.js b/backend/app/controllers/eventsController/socket/onDisconnect.js
index 8ff69ef..4cbf144 100644
--- a/backend/app/controllers/eventsController/socket/onDisconnect.js
+++ b/backend/app/controllers/eventsController/socket/onDisconnect.js
@@ -1,4 +1,4 @@
module.exports = (socket) => {
- console.log("Client disconnected");
- };
+ console.log('Client disconnected');
+};
\ No newline at end of file
diff --git a/backend/app/controllers/eventsController/socket/onRequestPin.js b/backend/app/controllers/eventsController/socket/onRequestPin.js
index fc4b80d..28bc29d 100644
--- a/backend/app/controllers/eventsController/socket/onRequestPin.js
+++ b/backend/app/controllers/eventsController/socket/onRequestPin.js
@@ -1,3 +1,3 @@
module.exports = (socket) => {
- console.log("Request pin");
+ console.log('Request pin');
};
\ No newline at end of file
diff --git a/backend/app/controllers/eventsController/socket/onSendPin.js b/backend/app/controllers/eventsController/socket/onSendPin.js
index 78b1cc9..32421be 100644
--- a/backend/app/controllers/eventsController/socket/onSendPin.js
+++ b/backend/app/controllers/eventsController/socket/onSendPin.js
@@ -1,14 +1,14 @@
-const { Tables } = require("../../../db/models");
+const { Tables } = require('../../../db/models');
module.exports = async (socket, data) => {
- const { pin, tableId } = data;
- if(!pin || !tableId) {
- socket.emit("tableLockStatus", { error: "Invalid pin or tableId" });
- return;
- }
+ const { pin, tableId } = data;
+ if(!pin || !tableId) {
+ socket.emit('tableLockStatus', { error: 'Invalid pin or tableId' });
+ return;
+ }
- const table = await Tables.findById(tableId);
- if(table.pin === pin) {
- socket.emit("tableLockStatus", { });
- }
+ const table = await Tables.findById(tableId);
+ if(table.pin === pin) {
+ socket.emit('tableLockStatus', { });
+ }
};
\ No newline at end of file
diff --git a/backend/app/controllers/ownerController.js b/backend/app/controllers/ownerController.js
index a165474..0974d5a 100644
--- a/backend/app/controllers/ownerController.js
+++ b/backend/app/controllers/ownerController.js
@@ -1,107 +1,106 @@
-const { closeTableAndTab, clearWaiter, getTableById, getTableByRestaurantId, getActiveOrders, updateOrder } = require('../datamodels');
-
-const updateOrderStatus = async (req, res) => {
- const { body: { status }, params: { orderId } } = req;
- if (!status || !orderId) {
- return res.status(404).send({ message: "Status and order id are required" });
- } else {
- try {
- const result = await updateOrder(orderId, status);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
- }
+const { closeTableAndTab, clearWaiterRequested, getTableById, getTableByRestaurantId, getActiveOrders, updateOrder } = require('../datamodels');
+
+const updateOrderController = async (req, res) => {
+ const { body: { status }, params: { orderId } } = req;
+ if (!status || !orderId) {
+ return res.status(404).send({ message: 'Status and order id are required' });
+ } else {
+ try {
+ const result = await updateOrder(orderId, status);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500);
+ }
+ }
};
-const returnActiveOrders = async (req, res) => {
- const { params: { restaurantId } } = req;
+const getActiveOrdersController = async (req, res) => {
+ const { params: { restaurantId } } = req;
- if (!restaurantId) {
- return res.status(404).send({ message: "Restaurant id is required" });
- }
+ if (!restaurantId) {
+ return res.status(404).send({ message: 'Restaurant id is required' });
+ }
- try {
- const result = await getActiveOrders(restaurantId);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
+ try {
+ const result = await getActiveOrders(restaurantId);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500);
+ }
};
-const getTables = async (req, res) => {
- const { params: { restaurantId } } = req;
+const getTableByRestaurantIdController = async (req, res) => {
+ const { params: { restaurantId } } = req;
- if (!restaurantId) {
- return res.status(404).send({ message: "Restaurant id is required" });
- }
+ if (!restaurantId) {
+ return res.status(404).send({ message: 'Restaurant id is required' });
+ }
- try {
- const result = await getTableByRestaurantId(restaurantId);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
+ try {
+ const result = await getTableByRestaurantId(restaurantId);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500);
+ }
};
-const getTable = async (req, res) => {
- const { params: { tableId } } = req;
+const getTableByIdController = async (req, res) => {
+ const { params: { tableId } } = req;
- if (!tableId) {
- return res.status(404).send({ message: "Table id is required" });
- }
+ if (!tableId) {
+ return res.status(404).send({ message: 'Table id is required' });
+ }
- try {
- const result = await getTableById(tableId);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
+ try {
+ const result = await getTableById(tableId);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500).send({ message: error.message });
+ }
};
-const closeTable = async (req, res) => {
- const { params: { tableId } } = req;
-
- if (!tableId) {
- return res.status(404).send({ message: "Table id is required" });
- }
-
- try {
- const result = await closeTableAndTab(tableId);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
-}
-
-const clearCallWaiter = async (req, res) => {
- const { params: { tabId } } = req;
-
- if (!tabId) {
- return res.status(404).send({ message: "Tab id is required" });
- }
-
- try {
- const result = await clearWaiter(tabId);
- res.send(result);
- } catch (error) {
- console.log(error);
- res.status(500).send({ message: error.message });
- }
+const closeTableController = async (req, res) => {
+ const { params: { tableId } } = req;
+
+ if (!tableId) {
+ return res.status(404).send({ message: 'Table id is required' });
+ }
+
+ try {
+ const result = await closeTableAndTab(tableId);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500);
+ }
+};
+
+const callWaiterController = async (req, res) => {
+ const { params: { tabId } } = req;
+
+ if (!tabId) {
+ return res.status(404).send({ message: 'Tab id is required' });
+ }
+ try {
+ const result = await clearWaiterRequested(tabId);
+ res.send(result);
+ } catch (error) {
+ console.log(error);
+ res.status(500);
+ }
};
module.exports = {
- updateOrder: updateOrderStatus,
- getActiveOrders: returnActiveOrders,
- getTable,
- getTables,
- clearCallWaiter,
- closeTable,
+ updateOrderController,
+ getActiveOrdersController,
+ getTableByRestaurantIdController,
+ getTableByIdController,
+ closeTableController,
+ callWaiterController,
};
diff --git a/backend/app/controllers/userController.js b/backend/app/controllers/userController.js
index f453044..ce5391c 100644
--- a/backend/app/controllers/userController.js
+++ b/backend/app/controllers/userController.js
@@ -1,231 +1,216 @@
-const { Tables, Products, Orders, Tabs } = require("../db/models");
-const { createNewOrder } = require('../datamodels');
-
-const getMenu = async (req, res) => {
- const id = req.params.id;
-
- const products = await Products.find(
- {
- restaurantId: id,
- type: {
- $in: ['product', 'variation']
- }
- }).populate('recipe').exec();
- res.send(products);
+const { createNewOrder, getProducts, getRandomTable, getTable, getTableStatus, getTab, getOrder, waiterRequested, paymentRequested } = require('../datamodels');
+
+const getMenuController = async (req, res) => {
+ if (!req.params.restaurantId) {
+ res.sendStatus(400);
+ } else {
+ const { params: { restaurantId } } = req;
+
+ try {
+ const products = await getProducts(restaurantId);
+ res.send(products);
+ }
+ catch (e) {
+ console.log(e);
+ res.sendStatus(404);
+ }
+ }
};
-const getRandomTable = async (req, res) => {
- if (!req.params.restaurantId) {
- res.sendStatus(400);
- } else {
- const { params: { restaurantId } } = req;
-
- const randomTable = await Tables.findOne({ restaurantId });
- const { _id = null } = randomTable || {};
- getTable(
- {
- ...req,
- params: { tableId: _id }
- },
- res);
- }
+const getRandomTableController = async (req, res) => {
+ if (!req.params.restaurantId) {
+ res.sendStatus(400);
+ } else {
+ const { params: { restaurantId } } = req;
+
+ try {
+ const table = await getRandomTable(restaurantId);
+ res.send(table);
+ }
+ catch (e) {
+ console.log(e);
+ res.sendStatus(404);
+ }
+ }
};
-const getTable = async (req, res) => {
- const { params: { tableId, pin } } = req;
-
- try {
- const table = await findTable(tableId);
-
- if (!table.tabOpen) {
- const newTab = await createNewTab(table);
- table.currentTab = newTab._id;
- table.tabOpen = true;
- await table.save();
- }
-
- // if (table.locked && table.pin !== pin) {
- // res.sendStatus(403);
- // } else {
- const populatedTable = await populateTableData(table);
- res.send(populatedTable);
- // }
- } catch (e) {
- console.log(e);
- if (e.message === 'Table not found') {
- res.sendStatus(404);
- } else {
- res.sendStatus(500);
- }
- }
+const getTableController = async (req, res) => {
+ const { params: { tableId, pin } } = req;
+
+ try {
+ const table = await getTable(tableId, pin);
+ res.send(table);
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Table not found') {
+ res.sendStatus(404);
+ } else if (e.message === 'Wrong pin') {
+ res.sendStatus(401);
+ }
+ else {
+ res.sendStatus(500);
+ }
+ }
};
-const getTab = async (req, res) => {
- const { params: { tabId } } = req;
-
- try {
- let tab = await Tabs.findById(tabId);
- if (!tab) {
- res.sendStatus(404);
- } else {
- tab = await tab.populate('orders').execPopulate();
- res.send(tab);
- }
- } catch (e) {
- console.log(e);
- res.sendStatus(500);
- }
+const getTableStatusController = async (req, res) => {
+ const { params: { tableId } } = req;
+ if (!tableId) {
+ res.sendStatus(404);
+ } else {
+ try {
+ await getTableStatus(tableId);
+ res.sendStatus(200);
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Table not found') {
+ res.sendStatus(404);
+ }
+ else if (e.message === 'Table is locked') {
+ res.sendStatus(401);
+ }
+ else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const newOrder = async (req, res) => {
- const { body: { cartProducts }, params: { tabId } } = req;
- try {
- await createNewOrder(cartProducts, tabId);
- res.sendStatus(200);
- } catch (e) {
- console.log(e);
- res.sendStatus(500);
- }
+const getTabController = async (req, res) => {
+ const { params: { tabId } } = req;
+
+ try {
+ const tab = await getTab(tabId);
+ res.send(tab);
+ }
+ catch (e) {
+ console.log(e);
+ if (e.message === 'Tab not found') {
+ res.sendStatus(404);
+ }
+ else {
+ res.sendStatus(500);
+ }
+
+ }
};
-const getOrder = async (req, res) => {
- const { params: { orderId } } = req;
- if (!orderId) {
- res.sendStatus(400);
- } else {
- try {
- const order = await Orders.findById(orderId);
- res.send(order);
- } catch (e) {
- res.send(500);
- }
- }
+const newOrderController = async (req, res) => {
+ const { body: { cartProducts }, params: { tabId } } = req;
+ if (!cartProducts || !tabId) {
+ res.sendStatus(400);
+ } else {
+ try {
+ await createNewOrder(cartProducts, tabId);
+ res.sendStatus(200);
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Tab not found') {
+ res.sendStatus(404);
+ }
+ else if (e.message === 'Tab closed') {
+ res.sendStatus(401);
+ }
+ else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const getTableStatus = async (req, res) => {
- const { params: { tableId } } = req;
- if (!tableId) {
- res.sendStatus(404);
- } else {
- try {
- const table = await Tables.findById(tableId);
- if (table.locked) {
- res.send({
- locked: "true",
- nextStep: "sendPin"
- });
- } else {
- res.send(table);
- }
- } catch (e) {
- res.send(500);
- }
- }
+const getOrderController = async (req, res) => {
+ const { params: { orderId } } = req;
+ if (!orderId) {
+ res.sendStatus(400);
+ } else {
+ try {
+ const order = await getOrder(orderId);
+ res.send(order);
+ }
+ catch (e) {
+ console.log(e);
+ if (e.message === 'Order not found') {
+ res.sendStatus(404);
+ }
+ else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const findTable = async (tableId) => {
- const table = await Tables.findById(tableId);
- if (!table) {
- throw new Error('Table not found');
- }
- return table;
+const callWaiterController = async (req, res) => {
+ const { params: { tabId } } = req;
+ if (!tabId) {
+ res.sendStatus(404);
+ } else {
+ try {
+ await waiterRequested(tabId);
+ res.sendStatus(200);
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Tab not found') {
+ res.sendStatus(404);
+ } else if (e.message === 'Tab closed') {
+ res.sendStatus(401);
+ } else if (e.message === 'Waiter already called') {
+ res.sendStatus(429);
+ } else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const createNewTab = async (table) => {
- const newTab = await new Tabs({
- restaurantId: table.restaurantId,
- tableId: table._id,
- status: "open",
- lastUpdated: Date.now(),
- createdAt: Date.now()
- }).save();
- return newTab;
+const payCardController = async (req, res) => {
+ const { params: { tabId } } = req;
+ if (!tabId) {
+ res.sendStatus(404);
+ } else {
+ try {
+ await paymentRequested(tabId, 'card');
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Tab not found') {
+ res.sendStatus(404);
+ } else if (e.message === 'Tab closed') {
+ res.sendStatus(401);
+ } else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const populateTableData = async (table) => {
- return await table.populate({
- path: 'currentTab',
- populate: {
- path: 'orders',
- options: { sort: { createdAt: -1 } },
- populate: {
- path: 'items'
- }
- }
- }).execPopulate();
+const payCashController = async (req, res) => {
+ const { params: { tabId } } = req;
+ if (!tabId) {
+ res.sendStatus(404);
+ } else {
+ try {
+ await paymentRequested(tabId, 'cash');
+ } catch (e) {
+ console.log(e);
+ if (e.message === 'Tab not found') {
+ res.sendStatus(404);
+ } else if (e.message === 'Tab closed') {
+ res.sendStatus(401);
+ } else {
+ res.sendStatus(500);
+ }
+ }
+ }
};
-const callWaiter = async (req, res) => {
- const { params: { tabId } } = req;
- if (!tabId) {
- res.sendStatus(404);
- } else {
- try {
- //find table that has OpenTab === tabId
- const tab = await Tabs.findById(tabId);
- if (!tab) {
- res.sendStatus(404);
- }
- tab.callWaiter = 'called';
- await tab.save();
- res.sendStatus(200);
- } catch (e) {
- res.sendStatus(500);
- }
- }
-};
-
-const payCard = async (req, res) => {
- const { params: { tabId } } = req;
- if (!tabId) {
- res.sendStatus(404);
- } else {
- try {
- //find table that has OpenTab === tabId
- const table = await Tables.findOne({ currentTab: tabId });
- if (!table) {
- res.sendStatus(404);
- }
- table.waiterCalled = true;
- await table.save();
- res.sendStatus(200);
- } catch (e) {
- console.log(e);
- res.sendStatus(500);
- }
- }
-};
-
-const payCash = async (req, res) => {
- const { params: { tabId } } = req;
- if (!tabId) {
- res.sendStatus(404);
- } else {
- try {
- const table = await Tables.findById(tableId);
- if (!table) {
- res.sendStatus(404);
- } else {
- table.waiterCalled = true;
- await table.save();
- res.send(table);
- }
- } catch (e) {
- res.send(500);
- }
- }
-};
-
-
-
module.exports = {
- getMenu,
- getRandomTable,
- getTable,
- getTab,
- newOrder,
- getOrder,
- callWaiter,
- payCard,
- payCash
+ getMenuController,
+ getRandomTableController,
+ getTableController,
+ getTableStatusController,
+ getTabController,
+ newOrderController,
+ getOrderController,
+ callWaiterController,
+ payCardController,
+ payCashController,
};
diff --git a/backend/app/datamodels/index.js b/backend/app/datamodels/index.js
index 16d3e18..f7af135 100644
--- a/backend/app/datamodels/index.js
+++ b/backend/app/datamodels/index.js
@@ -1,19 +1,35 @@
const { closeTableAndTab } = require('./tablesAndTabs');
-const { clearWaiter } = require('./tabs');
-const { getTableByRestaurantId, getTableById } = require('./tables');
-const { getActiveOrders, updateOrder, createNewOrder } = require('./orders');
-const { getRestaurantFromHostname, getRestaurantFromId, getRestaurantFromName } = require('./restaurants');
-
+const { getTab, closeTab, newTab, waiterRequested, clearWaiterRequested, paymentRequested } = require('./tabs');
+const { getTable, getRandomTable, closeTable, getTableStatus, getTableByRestaurantId, getTableById } = require('./tables');
+const { getOrder, getActiveOrders, updateOrder, createNewOrder } = require('./orders');
+const { getRestaurantByHostname, getRestaurantById, getRestaurantByName } = require('./restaurants');
+const { getProducts } = require('./products');
module.exports = {
- closeTableAndTab,
- clearWaiter,
- getTableByRestaurantId,
- getTableById,
- getActiveOrders,
- updateOrder,
- createNewOrder,
- getRestaurantFromHostname,
- getRestaurantFromId,
- getRestaurantFromName,
-}
\ No newline at end of file
+ closeTableAndTab,
+
+ getTab,
+ closeTab,
+ newTab,
+ waiterRequested,
+ clearWaiterRequested,
+ paymentRequested,
+
+ getTable,
+ getRandomTable,
+ closeTable,
+ getTableStatus,
+ getTableByRestaurantId,
+ getTableById,
+
+ getOrder,
+ getActiveOrders,
+ updateOrder,
+ createNewOrder,
+
+ getRestaurantById,
+ getRestaurantByName,
+ getRestaurantByHostname,
+
+ getProducts,
+};
\ No newline at end of file
diff --git a/backend/app/datamodels/orders.js b/backend/app/datamodels/orders.js
index 9606fbd..6699f97 100644
--- a/backend/app/datamodels/orders.js
+++ b/backend/app/datamodels/orders.js
@@ -1,99 +1,100 @@
-const mongoose = require('mongoose');
const { Orders, Tabs } = require('../db/models');
+const getOrder = async (orderId) => {
+ const order = await Orders.findById(orderId);
+ if (!order) {
+ throw new Error('Order not found');
+ }
+
+ return order;
+};
+
const getActiveOrders = async (restaurantId) => {
- const orders = await Orders.find({
- restaurantId,
- status: { $in: ["recieved", "inProgress"] }
- })
- .populate({
- path:'items',
- populate: {
- path: 'recipe',
- }
- })
- .populate({
- path: 'tabId',
- populate: {
- path: 'tableId'
- }
- }).exec();
- return orders;
+ const orders = await Orders.find({
+ restaurantId,
+ status: { $in: ['recieved', 'inProgress'] }
+ })
+ .populate({
+ path:'items',
+ populate: {
+ path: 'recipe',
+ }
+ })
+ .populate({
+ path: 'tabId',
+ populate: {
+ path: 'tableId'
+ }
+ }).exec();
+ return orders;
};
const updateOrder = async (orderId, status) => {
- if (!["received", "inProgress", "done", 'canceled'].some((msg) => msg === status)) {
- throw new Error('Invalid status');
- } else {
- try {
- const order = await Orders.findById(orderId);
- if (!order) {
- throw new Error('Order not found');
- } else {
- order.status = status;
- await order.save();
- return order;
- }
- } catch (e) {
- throw new Error('Error updating order');
- }
- }
+ if (!['received', 'inProgress', 'done', 'canceled'].some((msg) => msg === status)) {
+ throw new Error('Invalid status');
+ } else {
+ try {
+ const order = await Orders.findById(orderId);
+ if (!order) {
+ throw new Error('Order not found');
+ } else {
+ order.status = status;
+ await order.save();
+ return order;
+ }
+ } catch (e) {
+ throw new Error('Error updating order');
+ }
+ }
};
const createNewOrder = async (cartProducts, tabId) => {
- if (!cartProducts || !tabId) {
- throw new Error('Invalid request');
- } else {
- try {
- const tab = await Tabs.findById(tabId);
- if (!tab) {
- throw new Error('Tab not found');
- } else if (tab.status === 'closed') {
- throw new Error('Tab closed');
- } else {
- const totalAmount = cartProducts.reduce(
- (total, cartProduct) => {
- if(cartProduct.type === "product"){
- return total + parseInt(cartProduct.cartQty) * parseFloat(cartProduct.price.toFixed(2));
- }
- else if(cartProduct.type === "variation") {
- return total + parseInt(cartProduct.cartQty) * parseFloat(cartProduct.recipe[0].price.toFixed(2));
- }
- else return 0;
- }, 0);
+ const tab = await Tabs.findById(tabId);
+ if (!tab) {
+ throw new Error('Tab not found');
+ } else if (tab.status === 'closed') {
+ throw new Error('Tab closed');
+ } else {
+ const totalAmount = cartProducts.reduce(
+ (total, cartProduct) => {
+ if(cartProduct.type === 'product'){
+ return total + parseInt(cartProduct.cartQty) * parseFloat(cartProduct.price.toFixed(2));
+ }
+ else if(cartProduct.type === 'variation') {
+ return total + parseInt(cartProduct.cartQty) * parseFloat(cartProduct.recipe[0].price.toFixed(2));
+ }
+ else return 0;
+ }, 0);
- //grab all the product ids from the products array and put them into a new array. if a product has a cartQty of 3, it will be added 3 times
- const items = cartProducts.flatMap(({ cartQty, _id }) => Array(cartQty).fill(_id));
+ //grab all the product ids from the products array and put them into a new array. if a product has a cartQty of 3, it will be added 3 times
+ const items = cartProducts.flatMap(({ cartQty, _id }) => Array(cartQty).fill(_id));
- //grab all the cartProducts that are variations and the recipie[0] in an object with the cart product id and recipie[0]._id
- const variations = cartProducts.flatMap(({_id, type, recipe, cartQty}) => {
- if(type === "variation") {
- return Array(recipe[0].cartQty).fill({id: _id, recipe: recipe[0]._id, qty: cartQty});
- }
- else return [];
- });
+ //grab all the cartProducts that are variations and the recipie[0] in an object with the cart product id and recipie[0]._id
+ const variations = cartProducts.flatMap(({_id, type, recipe, cartQty}) => {
+ if(type === 'variation') {
+ return Array(recipe[0].cartQty).fill({id: _id, recipe: recipe[0]._id, qty: cartQty});
+ }
+ else return [];
+ });
- const order = new Orders({
- tabId: tab._id,
- restaurantId: tab.restaurantId,
- totalAmount,
- items,
- variations
- });
- tab.orders.push(order._id);
+ const order = new Orders({
+ tabId: tab._id,
+ restaurantId: tab.restaurantId,
+ totalAmount,
+ items,
+ variations
+ });
+ tab.orders.push(order._id);
- await Promise.all([order.save(), tab.save()]);
- return order;
+ await Promise.all([order.save(), tab.save()]);
+ return order;
- }
- } catch (e) {
- throw new Error('Error creating order');
- }
- }
+ }
};
module.exports = {
- getActiveOrders,
- updateOrder,
- createNewOrder,
+ getOrder,
+ getActiveOrders,
+ updateOrder,
+ createNewOrder,
};
\ No newline at end of file
diff --git a/backend/app/datamodels/products.js b/backend/app/datamodels/products.js
new file mode 100644
index 0000000..0ec0c71
--- /dev/null
+++ b/backend/app/datamodels/products.js
@@ -0,0 +1,17 @@
+const { Products } = require('../db/models');
+
+const getProducts = async (restaurantId) => {
+ const products = await Products.find({
+ restaurantId,
+ type: {
+ $in: ['product', 'variation']
+ }
+ })
+ .populate('recipe').exec();
+
+ return products;
+};
+
+module.exports = {
+ getProducts
+};
diff --git a/backend/app/datamodels/restaurants.js b/backend/app/datamodels/restaurants.js
index e324621..c234f37 100644
--- a/backend/app/datamodels/restaurants.js
+++ b/backend/app/datamodels/restaurants.js
@@ -1,42 +1,42 @@
-const { Restaurants } = require("../db/models");
+const { Restaurants } = require('../db/models');
-const getRestaurantFromId = async (restaurantId) => {
- const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ _id: restaurantId});
+const getRestaurantById = async (restaurantId) => {
+ const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ _id: restaurantId});
- return {
- restaurantId: _id,
- restaurantName,
- ownerName,
- maxInstances
- }
- };
+ return {
+ restaurantId: _id,
+ restaurantName,
+ ownerName,
+ maxInstances
+ };
+};
- const getRestaurantFromName = async (restaurantNameFromUrl) => {
- const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ name: restaurantNameFromUrl});
+const getRestaurantByName = async (restaurantNameFromUrl) => {
+ const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ name: restaurantNameFromUrl});
- return {
- restaurantId: _id,
- restaurantName,
- ownerName,
- maxInstances
- }
- };
+ return {
+ restaurantId: _id,
+ restaurantName,
+ ownerName,
+ maxInstances
+ };
+};
- const getRestaurantFromHostname = async (hostname) => {
- const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ externalHostnames: hostname })
+const getRestaurantByHostname = async (hostname) => {
+ const { _id, name: restaurantName, ownerName, maxInstances } = await Restaurants.findOne({ externalHostnames: hostname });
- return {
- restaurantId: _id,
- restaurantName,
- ownerName,
- maxInstances
- }
- };
+ return {
+ restaurantId: _id,
+ restaurantName,
+ ownerName,
+ maxInstances
+ };
+};
- module.exports = {
- getRestaurantFromHostname,
- getRestaurantFromId,
- getRestaurantFromName
- };
+module.exports = {
+ getRestaurantById,
+ getRestaurantByName,
+ getRestaurantByHostname,
+};
\ No newline at end of file
diff --git a/backend/app/datamodels/tables.js b/backend/app/datamodels/tables.js
index ada29d0..3c7633b 100644
--- a/backend/app/datamodels/tables.js
+++ b/backend/app/datamodels/tables.js
@@ -1,72 +1,139 @@
-const mongoose = require('mongoose');
const { Tables } = require('../db/models');
+const { newTab } = require('./tabs');
+
+const getRandomTable = async (restaurantId) => {
+ const randomTable = await Tables.findOne({ restaurantId });
+ if (!randomTable) {
+ throw new Error('No tables found for this restaurant');
+ } else {
+ const table = await getTable(randomTable._id);
+ return table;
+ }
+};
+
+const getTable = async (tableId, pin) => {
+ const table = await Tables.findById(tableId);
+
+ if (!table) {
+ throw new Error('Table not found');
+ }
+
+ if (table.locked && table.pin !== pin) {
+ throw new Error('Incorrect pin');
+ }
+
+ if (!table.tabOpen) {
+ const currentNewTab = await newTab(table);
+ table.currentTab = currentNewTab._id;
+ table.tabOpen = true;
+ await table.save();
+ }
+
+ const populatedTable = await populateTableData(table);
+
+ return populatedTable;
+};
const closeTable = async (tableId, session) => {
- const table = await Tables.findById(tableId).session(session);
+ const table = await Tables.findById(tableId).session(session);
- if (!table) {
- throw new Error('Table not found');
- }
+ if (!table) {
+ throw new Error('Table not found');
+ }
- if (!table.currentTab) {
- throw new Error('No current tab for the table');
- }
- const currentTab = table.currentTab;
- table.olderTabs.push(table.currentTab);
- table.currentTab = null;
- table.tabOpen = false;
- table.lastUpdated = Date.now();
+ if (!table.currentTab) {
+ throw new Error('No current tab for the table');
+ }
+ const currentTab = table.currentTab;
+ table.olderTabs.push(table.currentTab);
+ table.currentTab = null;
+ table.tabOpen = false;
+ table.lastUpdated = Date.now();
- await table.save({ session });
+ await table.save({ session });
- return { table, tabId: currentTab }
+ return { table, tabId: currentTab };
};
const getTableById = async (tableId) => {
- const table = await Tables.findById(tableId).populate({
- path: 'currentTab',
- populate: {
- path: 'orders',
- populate: {
- path: 'items',
- populate: {
- path: 'recipe'
- }
- }
- }
- }).exec();
-
- if (!table) {
- throw new Error('Table not found');
- }
-
- return table;
+ const table = await Tables.findById(tableId).populate({
+ path: 'currentTab',
+ populate: {
+ path: 'orders',
+ populate: {
+ path: 'items',
+ populate: {
+ path: 'recipe'
+ }
+ }
+ }
+ }).exec();
+
+ if (!table) {
+ throw new Error('Table not found');
+ }
+
+ return table;
};
const getTableByRestaurantId = async (restaurantId) => {
- const table = await Tables.find({ restaurantId }).populate({
- path: 'currentTab',
- populate: {
- path: 'orders',
- populate: {
- path: 'items',
- populate: {
- path: 'recipe'
- }
- }
- }
- }).exec();
-
- if (!table) {
- throw new Error('Table not found');
- }
-
- return table;
+ const table = await Tables.find({ restaurantId }).populate({
+ path: 'currentTab',
+ populate: {
+ path: 'orders',
+ populate: {
+ path: 'items',
+ populate: {
+ path: 'recipe'
+ }
+ }
+ }
+ }).exec();
+
+ if (!table) {
+ throw new Error('Table not found');
+ }
+
+ return table;
+};
+
+const getTableStatus = async (tableId) => {
+ const table = await Tables.findById(tableId);
+
+ if (!table) {
+ throw new Error('Table not found');
+ }
+
+ if (table.locked) {
+ throw new Error('Table is locked');
+ }
+
+ return true;
};
+const populateTableData = async (table) => {
+ return await table.populate({
+ path: 'currentTab',
+ populate: {
+ path: 'orders',
+ options: { sort: { createdAt: -1 } },
+ populate: {
+ path: 'items',
+ populate: {
+ path: 'recipe'
+ }
+ }
+ }
+ }).execPopulate();
+};
module.exports = {
- closeTable,
- getTableByRestaurantId,
- getTableById,
+ getTable,
+ getRandomTable,
+ closeTable,
+ getTableStatus,
+ getTableByRestaurantId,
+ getTableById,
};
+
+// TODO: refactor the getTableById and getTable to only one function and make necceary changes in the frontend
\ No newline at end of file
diff --git a/backend/app/datamodels/tablesAndTabs.js b/backend/app/datamodels/tablesAndTabs.js
index fb1d8a9..8176277 100644
--- a/backend/app/datamodels/tablesAndTabs.js
+++ b/backend/app/datamodels/tablesAndTabs.js
@@ -3,28 +3,28 @@ const { closeTable } = require('./tables');
const { closeTab } = require('./tabs');
const closeTableAndTab = async (tableId) => {
- let session = await mongoose.startSession();
+ let session = await mongoose.startSession();
- try {
- session.startTransaction();
+ try {
+ session.startTransaction();
- const { table, tabId } = await closeTable(tableId, session);
+ const { table, tabId } = await closeTable(tableId, session);
- await closeTab(tabId, session);
+ await closeTab(tabId, session);
- await session.commitTransaction();
+ await session.commitTransaction();
- return { message: "Closed table" };
- } catch (error) {
- console.log('Transaction aborted');
- console.log(error);
- await session.abortTransaction();
- throw error;
- } finally {
- session.endSession();
- }
-}
+ return { message: 'Closed table' };
+ } catch (error) {
+ console.log('Transaction aborted');
+ console.log(error);
+ await session.abortTransaction();
+ throw error;
+ } finally {
+ session.endSession();
+ }
+};
module.exports = {
- closeTableAndTab,
+ closeTableAndTab,
};
diff --git a/backend/app/datamodels/tabs.js b/backend/app/datamodels/tabs.js
index c0e9224..4a3b82e 100644
--- a/backend/app/datamodels/tabs.js
+++ b/backend/app/datamodels/tabs.js
@@ -1,35 +1,95 @@
-const mongoose = require('mongoose');
const { Tabs } = require('../db/models');
+const getTab = async (tabId) => {
+ let tab = await Tabs.findById(tabId);
+ if (!tab) {
+ throw new Error('Tab not found');
+ } else {
+ tab = await tab.populate('orders').execPopulate();
+ return tab;
+ }
+};
+
const closeTab = async (tabId, session) => {
- const tab = await Tabs.findById(tabId).session(session);
+ const tab = await Tabs.findById(tabId).session(session);
+
+ if (!tab) {
+ throw new Error('Tab not found');
+ }
- if (!tab) {
- throw new Error('Tab not found');
- }
+ tab.status = 'closed';
+ tab.lastUpdated = Date.now();
- tab.status = 'closed';
- tab.lastUpdated = Date.now();
+ await tab.save({ session });
- await tab.save({ session });
+ return tab;
+};
- return tab;
-}
+const clearWaiterRequested = async (tabId) => {
+ const tab = await Tabs.findById(tabId);
-const clearWaiter = async (tabId) => {
- const tab = await Tabs.findById(tabId);
+ if (!tab) {
+ throw new Error('Tab not found');
+ }
- if (!tab) {
- throw new Error('Tab not found');
- }
+ tab.callWaiter = null;
+ await tab.save();
- tab.callWaiter = null;
- await tab.save();
+ return tab;
+};
- return tab;
-}
+const waiterRequested = async (tabId) => {
+ const tab = await Tabs.findById(tabId);
+ if (!tab) {
+ throw new Error('Tab not found');
+ }
+ if (tab.callWaiter === 'called') {
+ throw new Error('Waiter already called');
+ }
+ if (tab.status === 'closed') {
+ throw new Error('Tab closed');
+ } else {
+ tab.callWaiter = 'called';
+ await tab.save();
+
+ return true;
+ }
+};
+
+const paymentRequested = async (tabId, paymentType, tipsPercent) => {
+ const tab = await Tabs.findById(tabId);
+ if (!tab) {
+ throw new Error('Tab not found');
+ }
+ if (tab.status === 'closed') {
+ throw new Error('Tab closed');
+ }
+
+ tab.paymentRequested = paymentType;
+ if (tipsPercent) {
+ tab.tipsPercent = tipsPercent;
+ }
+ await tab.save();
+
+ return true;
+};
+
+const newTab = async (table) => {
+ const newTab = await new Tabs({
+ restaurantId: table.restaurantId,
+ tableId: table._id,
+ status: 'open',
+ lastUpdated: Date.now(),
+ createdAt: Date.now()
+ }).save();
+ return newTab;
+};
module.exports = {
- closeTab,
- clearWaiter,
+ getTab,
+ closeTab,
+ newTab,
+ waiterRequested,
+ clearWaiterRequested,
+ paymentRequested
};
diff --git a/backend/app/db/collectionWatcher.js b/backend/app/db/collectionWatcher.js
index 9673765..60152af 100644
--- a/backend/app/db/collectionWatcher.js
+++ b/backend/app/db/collectionWatcher.js
@@ -2,30 +2,30 @@ const MongoClient = require('mongodb').MongoClient;
const EventEmitter = require('events');
class CollectionWatcher extends EventEmitter {
- constructor() {
- super();
- this.collections = [];
- }
+ constructor() {
+ super();
+ this.collections = [];
+ }
- async init(clientUrl) {
- this.client = await MongoClient.connect(clientUrl);
- }
- subscribeToCollection(collection) {
- const mongoCollection = this.client.db('rockandrolla').collection(collection);
- const collectionStream = mongoCollection.watch();
+ async init(clientUrl) {
+ this.client = await MongoClient.connect(clientUrl);
+ }
+ subscribeToCollection(collection) {
+ const mongoCollection = this.client.db('tabley').collection(collection);
+ const collectionStream = mongoCollection.watch();
- this.collections.push(
- {
- [collection]: collectionStream
- });
+ this.collections.push(
+ {
+ [collection]: collectionStream
+ });
- collectionStream.on('change', (change) => {
- this.emit(`${collection}:change`, change);
- });
- }
+ collectionStream.on('change', (change) => {
+ this.emit(`${collection}:change`, change);
+ });
+ }
}
module.exports = {
- CollectionWatcher
-}
\ No newline at end of file
+ CollectionWatcher
+};
\ No newline at end of file
diff --git a/backend/app/db/connection.js b/backend/app/db/connection.js
index 6deb0aa..851ed24 100644
--- a/backend/app/db/connection.js
+++ b/backend/app/db/connection.js
@@ -1,15 +1,15 @@
-const mongoose = require("mongoose");
+const mongoose = require('mongoose');
-const dbUrl = `mongodb://${process.env.MONGODB_HOST || "localhost"}/tabley`;
+const dbUrl = `mongodb://${process.env.MONGODB_HOST || 'localhost'}/tabley`;
async function connectToDatabase() {
- await mongoose.connect(dbUrl, {
- useNewUrlParser: true,
- useUnifiedTopology: true,
- });
+ await mongoose.connect(dbUrl, {
+ useNewUrlParser: true,
+ useUnifiedTopology: true,
+ });
}
module.exports = {
- connectToDatabase,
- dbUrl
- };
+ connectToDatabase,
+ dbUrl
+};
diff --git a/backend/app/db/models.js b/backend/app/db/models.js
index 93cbc69..75d80af 100644
--- a/backend/app/db/models.js
+++ b/backend/app/db/models.js
@@ -3,138 +3,138 @@ const { Schema: { Types: { ObjectId } } } = mongoose;
const { model, Schema } = mongoose;
const restaurantSchema = Schema({
- name: String,
- ownerName: String,
- externalHostnames: Array,
- maxInstances: Number, // max ws listeners
- //open hours - todo for last order before!
+ name: String,
+ ownerName: String,
+ externalHostnames: Array,
+ maxInstances: Number, // max ws listeners
+ //open hours - todo for last order before!
});
const tableSchema = Schema({
- restaurantId: String,
- tableNo: Number,
- tabOpen: Boolean,
- options: Array,
- currentTab: {
- type: ObjectId,
- ref: 'Tabs'
- },
- olderTabs: [{
- type: ObjectId,
- ref: 'Tabs'
- }],
- locked: Boolean,
- pin: String,
- lastUpdated: Date
+ restaurantId: String,
+ tableNo: Number,
+ tabOpen: Boolean,
+ options: Array,
+ currentTab: {
+ type: ObjectId,
+ ref: 'Tabs'
+ },
+ olderTabs: [{
+ type: ObjectId,
+ ref: 'Tabs'
+ }],
+ locked: Boolean,
+ pin: String,
+ lastUpdated: Date
});
const productSchema = Schema({
- restaurantId: {
- type: Schema.Types.ObjectId,
- ref: 'Restaurants'
- },
+ restaurantId: {
+ type: Schema.Types.ObjectId,
+ ref: 'Restaurants'
+ },
- name: String,
- imgUrl: String,
+ name: String,
+ imgUrl: String,
- type: String, //product, reciepieProduct, variation
+ type: String, //product, reciepieProduct, variation
- price: Number,
- currency: String,
+ price: Number,
+ currency: String,
- description: String,
+ description: String,
- stock: Number,
- active: Boolean,
+ stock: Number,
+ active: Boolean,
- alcoholContent: String,
+ alcoholContent: String,
- measureUnit: String,
- qty: Number,
+ measureUnit: String,
+ qty: Number,
- recipe: [{
- type: Schema.Types.ObjectId,
- ref: 'Products'
- }],
+ recipe: [{
+ type: Schema.Types.ObjectId,
+ ref: 'Products'
+ }],
- category: String,
- subCategory: String,
-
- stock: Number,
+ category: String,
+ subCategory: String,
});
const tabSchema = Schema({
- restaurantId: {
- type: ObjectId,
- ref: 'Restaurants'
- },
- tableId: {
- type: ObjectId,
- ref: 'Tables'
- },
- orders: [
- {
- type: ObjectId,
- ref: 'Orders'
- }
- ],
- status: String, // request-to-close,closed,open
- callWaiter: String, // called, pending-arival, not-called
- lastUpdated: Date,
- createdAt: Date
+ restaurantId: {
+ type: ObjectId,
+ ref: 'Restaurants'
+ },
+ tableId: {
+ type: ObjectId,
+ ref: 'Tables'
+ },
+ orders: [
+ {
+ type: ObjectId,
+ ref: 'Orders'
+ }
+ ],
+ status: String, // request-to-close,closed,open
+ callWaiter: String, // called; improve this to an array containing an object with timestamps when the waiter was called, and when the waiter cleared the call
+ paymentRequested: String, // cash, card
+ tipsPercent: Number,
+ lastUpdated: Date,
+ createdAt: Date
});
const orderSchema = Schema({
- status: {
- type:String,
- default: 'recieved'
- }, // received, inProgress, done, canceled
- restaurantId: {
- type: ObjectId,
- ref: 'Restaurants'
- },
- tabId: {
- type: ObjectId,
- ref: 'Tabs'
- },
- items: [{
- type: ObjectId,
- ref: 'Products'
- }],
- variations: [],
- totalAmount: Number,
- createdAt: {
- type: Date,
- default: Date.now
- },
- lastUpdated: {
- type: Date,
- default: Date.now
- }
+ status: {
+ type:String,
+ default: 'recieved'
+ }, // received, inProgress, done, canceled
+ restaurantId: {
+ type: ObjectId,
+ ref: 'Restaurants'
+ },
+ tabId: {
+ type: ObjectId,
+ ref: 'Tabs'
+ },
+ items: [{
+ type: ObjectId,
+ ref: 'Products'
+ }],
+ variations: [],
+ totalAmount: Number,
+ createdAt: {
+ type: Date,
+ default: Date.now
+ },
+ lastUpdated: {
+ type: Date,
+ default: Date.now
+ }
});
const waiterSchema = Schema({
- restaurantId: {
- type: ObjectId,
- ref: 'Restaurants'
- },
- name: String,
- enabled: Boolean,
- createdAt: Date
+ restaurantId: {
+ type: ObjectId,
+ ref: 'Restaurants'
+ },
+ name: String,
+ enabled: Boolean,
+ createdAt: Date
});
-const Restaurants = model("Restaurants", restaurantSchema);
-const Tables = model("Tables", tableSchema);
-const Products = model("Products", productSchema);
-const Tabs = model("Tabs", tabSchema);
-const Orders = model("Orders", orderSchema);
-const Waiters = model("Waiters", waiterSchema);
+const Restaurants = model('Restaurants', restaurantSchema);
+const Tables = model('Tables', tableSchema);
+const Products = model('Products', productSchema);
+const Tabs = model('Tabs', tabSchema);
+const Orders = model('Orders', orderSchema);
+const Waiters = model('Waiters', waiterSchema);
module.exports = {
- Restaurants,
- Tables,
- Products,
- Orders,
- Tabs,
- Waiters
-}
\ No newline at end of file
+ Restaurants,
+ Tables,
+ Products,
+ Orders,
+ Tabs,
+ Waiters
+};
\ No newline at end of file
diff --git a/backend/app/handlers/events.js b/backend/app/handlers/events.js
index 0ee7ea9..a8e84a8 100644
--- a/backend/app/handlers/events.js
+++ b/backend/app/handlers/events.js
@@ -1,39 +1,39 @@
-const { CollectionWatcher } = require("../db/collectionWatcher");
-const { dbUrl } = require("../db/connection");
+const { CollectionWatcher } = require('../db/collectionWatcher');
+const { dbUrl } = require('../db/connection');
-const onConnection = require("../controllers/eventsController/socket/onConnect");
-const onDisconnect = require("../controllers/eventsController/socket/onDisconnect");
+const onConnection = require('../controllers/eventsController/socket/onConnect');
+const onDisconnect = require('../controllers/eventsController/socket/onDisconnect');
-const onOrderChange = require("../controllers/eventsController/db/onOrderChange");
-const onTabChange = require("../controllers/eventsController/db/onTabChange");
+const onOrderChange = require('../controllers/eventsController/db/onOrderChange');
+const onTabChange = require('../controllers/eventsController/db/onTabChange');
const createCollectionWatcher = async (io) => {
- const mongoEvents = new CollectionWatcher();
- await mongoEvents.init(dbUrl);
- mongoEvents.subscribeToCollection("orders");
- mongoEvents.subscribeToCollection("tabs");
+ const mongoEvents = new CollectionWatcher();
+ await mongoEvents.init(dbUrl);
+ mongoEvents.subscribeToCollection('orders');
+ mongoEvents.subscribeToCollection('tabs');
- mongoEvents.on("orders:change", (mongoEvent) => {
- onOrderChange(mongoEvent, io);
- });
+ mongoEvents.on('orders:change', (mongoEvent) => {
+ onOrderChange(mongoEvent, io);
+ });
- mongoEvents.on("tabs:change", (mongoEvent) => {
- onTabChange(mongoEvent, io);
- });
+ mongoEvents.on('tabs:change', (mongoEvent) => {
+ onTabChange(mongoEvent, io);
+ });
};
const socketIOEventsHandler = (io) => {
- io.on("connection", (socket) => {
- onConnection(socket);
+ io.on('connection', (socket) => {
+ onConnection(socket);
- socket.on("disconnect", () => {
- onDisconnect(socket);
- });
- });
+ socket.on('disconnect', () => {
+ onDisconnect(socket);
+ });
+ });
};
module.exports = {
- createCollectionWatcher,
- createSocketHandler: socketIOEventsHandler,
+ createCollectionWatcher,
+ createSocketHandler: socketIOEventsHandler,
};
\ No newline at end of file
diff --git a/backend/app/handlers/requests.js b/backend/app/handlers/requests.js
index 6dff6b8..1737e3f 100644
--- a/backend/app/handlers/requests.js
+++ b/backend/app/handlers/requests.js
@@ -1,55 +1,71 @@
-const path = require("path");
+const path = require('path');
const compression = require('compression');
-const express = require("express");
-const cors = require("cors");
-const helmet = require("helmet");
+const express = require('express');
+const basicAuth = require('express-basic-auth');
+const cors = require('cors');
+const helmet = require('helmet');
const frontendFilesPath = path.join(__dirname, '../../../frontend/web-build');
const createApp = (dependencies = {}) => {
- const app = express();
-
- let origins = require("../../cors.json").origins;
- console.log(origins);
- setInterval(() => {
- try{
- delete require.cache[require.resolve("../../cors.json")];
- origins = require("../../cors.json").origins;
- } catch(e) {
- console.log("Express: Error reading cors.json file");
- }
- }, 60000);
-
- app.use(compression({ filter: (req, res) => true, level: 6, algorithms: ['br', 'gzip', 'deflate'] }));
- app.use(cors({
- origin: function (origin, callback) {
- if (!origin) return callback(null, true);
- if (origins.indexOf(origin) !== -1) {
- callback(null, true)
- } else {
- console.log("Express: Blocked by CORS: " + origin);
- callback('Not allowed by CORS')
- }
- }
- }));
- app.use(express.json());
- app.use(helmet({
- contentSecurityPolicy: {
- directives: {
- defaultSrc: ["'self'", "https://unpkg.com", "'unsafe-inline'", "https://123rf.com"],
- scriptSrc: ["'self'", "'unsafe-inline'", "'wasm-unsafe-eval'"],
- },
- },
- }));
-
- app.use("/api/user", dependencies.userRoutes || require("../routes/userRoutes"));
- app.use("/api/owner", dependencies.ownerRoutes || require("../routes/ownerRoutes"));
- app.use("/api/status", dependencies.statusRoutes || require("../routes/statusRoutes"));
- app.use("/api/app", dependencies.appRoutes || require("../routes/appRoutes"));
- app.use("/", express.static(frontendFilesPath));
- return app;
-}
+ const app = express();
+
+ let origins = require('../../cors.json').origins;
+ console.log(origins);
+ setInterval(() => {
+ try{
+ delete require.cache[require.resolve('../../cors.json')];
+ origins = require('../../cors.json').origins;
+ } catch(e) {
+ console.log('Express: Error reading cors.json file');
+ }
+ }, 60000);
+
+ app.use(compression({ filter: () => true, level: 6, algorithms: ['br', 'gzip', 'deflate'] }));
+ app.use(cors({
+ origin: function (origin, callback) {
+ if (!origin) return callback(null, true);
+ if (origins.indexOf(origin) !== -1) {
+ callback(null, true);
+ } else {
+ console.log('Express: Blocked by CORS: ' + origin);
+ callback('Not allowed by CORS');
+ }
+ }
+ }));
+ app.use(express.json());
+ app.use(helmet({
+ contentSecurityPolicy: {
+ directives: {
+ defaultSrc: ['\'self\'', 'https://unpkg.com', '\'unsafe-inline\'', 'https://123rf.com'],
+ scriptSrc: ['\'self\'', '\'unsafe-inline\'', '\'wasm-unsafe-eval\'', 'blob:', 'https://cdn.jsdelivr.net'],
+ imgSrc: ['\'self\'', 'https://previews.123rf.com']
+ },
+ },
+ }));
+
+ app.use('/api/user', dependencies.userRoutes || require('../routes/userRoutes'));
+ app.use('/api/owner', dependencies.ownerRoutes || require('../routes/ownerRoutes'));
+ app.use('/api/status', dependencies.statusRoutes || require('../routes/statusRoutes'));
+ app.use('/api/app', dependencies.appRoutes || require('../routes/appRoutes'));
+
+ app.use(
+ '/@:random/restaurant-view',
+ basicAuth({
+ users: { '': '' },
+ challenge: true,
+ realm: 'tabley',
+ }),
+ (req) => req.url = '',
+ express.static(frontendFilesPath),
+ );
+ app.use('/@:random', (req) => req.url = '', express.static(frontendFilesPath));
+ app.use('/join', (req) => req.url = '', express.static(frontendFilesPath));
+ app.use('/', express.static(frontendFilesPath));
+
+ return app;
+};
module.exports = {
- createApp
-}
\ No newline at end of file
+ createApp
+};
\ No newline at end of file
diff --git a/backend/app/index.js b/backend/app/index.js
index c440c72..10bdd73 100644
--- a/backend/app/index.js
+++ b/backend/app/index.js
@@ -1,8 +1,8 @@
-const { createApp } = require("./handlers/requests");
-const { createCollectionWatcher, createSocketHandler } = require("./handlers/events");
+const { createApp } = require('./handlers/requests');
+const { createCollectionWatcher, createSocketHandler } = require('./handlers/events');
module.exports = {
- createApp,
- createCollectionWatcher,
- createSocketHandler
+ createApp,
+ createCollectionWatcher,
+ createSocketHandler
};
\ No newline at end of file
diff --git a/backend/app/routes/appRoutes.js b/backend/app/routes/appRoutes.js
index 696cfae..9e0029a 100644
--- a/backend/app/routes/appRoutes.js
+++ b/backend/app/routes/appRoutes.js
@@ -1,10 +1,10 @@
-const express = require("express");
-const { getRestaurantById, getRestaurantByName, hostnameCheck } = require("../controllers/appController");
+const express = require('express');
+const { getRestaurantByIdController, getRestaurantByNameController, getRestaurantByHostnameController } = require('../controllers/appController');
const router = express.Router();
-router.get("/restaurantName/:restaurantName", getRestaurantByName);
-router.get("/restaurantId/:restaurantId", getRestaurantById);
-router.post("/hostnameCheck", hostnameCheck);
+router.get('/restaurantId/:restaurantId', getRestaurantByIdController);
+router.get('/restaurantName/:restaurantName', getRestaurantByNameController);
+router.post('/hostnameCheck', getRestaurantByHostnameController);
module.exports = router;
diff --git a/backend/app/routes/ownerRoutes.js b/backend/app/routes/ownerRoutes.js
index 9b6fb5a..520d67f 100644
--- a/backend/app/routes/ownerRoutes.js
+++ b/backend/app/routes/ownerRoutes.js
@@ -1,15 +1,15 @@
-const express = require("express");
-const { updateOrder, getActiveOrders, getTables, getTable, closeTable, clearCallWaiter } = require("../controllers/ownerController");
+const express = require('express');
+const { updateOrderController, getActiveOrdersController, getTableByRestaurantIdController, getTableByIdController, closeTableController, callWaiterController } = require('../controllers/ownerController');
const router = express.Router();
-router.post("/orders/update/:orderId", updateOrder);
-router.get("/orders/active/:restaurantId", getActiveOrders);
+router.post('/orders/update/:orderId', updateOrderController);
+router.get('/orders/active/:restaurantId', getActiveOrdersController);
-router.get("/tables/:restaurantId", getTables);
+router.get('/tables/:restaurantId', getTableByRestaurantIdController);
-router.get("/table/:tableId", getTable);
-router.get("/table/close/:tableId", closeTable);
+router.get('/table/:tableId', getTableByIdController);
+router.get('/table/close/:tableId', closeTableController);
-router.get("/actions/clearCallWaiter/:tabId", clearCallWaiter);
+router.get('/actions/clearCallWaiter/:tabId', callWaiterController);
module.exports = router;
diff --git a/backend/app/routes/statusRoutes.js b/backend/app/routes/statusRoutes.js
index 8422da6..b7b4647 100644
--- a/backend/app/routes/statusRoutes.js
+++ b/backend/app/routes/statusRoutes.js
@@ -1,9 +1,9 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
-router.get("/", (req, res) => {
- res.sendStatus(200);
+router.get('/', (req, res) => {
+ res.sendStatus(200);
});
module.exports = router;
diff --git a/backend/app/routes/userRoutes.js b/backend/app/routes/userRoutes.js
index e47061a..de55b76 100644
--- a/backend/app/routes/userRoutes.js
+++ b/backend/app/routes/userRoutes.js
@@ -1,21 +1,25 @@
-const express = require("express");
-const { getMenu, getRandomTable, getTable, getTab, newOrder, getOrder, callWaiter, payCard, payCash } = require("../controllers/userController");
+const express = require('express');
+const { getMenuController, getRandomTableController, getTableController, getTableStatusController, getTabController, newOrderController, getOrderController, callWaiterController, payCardController, payCashController } = require('../controllers/userController');
+const PROD = process.env.NODE_ENV === 'production';
const router = express.Router();
-router.get("/menus/:id", getMenu);
+router.get('/menus/:restaurantId', getMenuController);
-router.get("/getRandomTable/:restaurantId", getRandomTable);
+if (!PROD) {
+ router.get('/getRandomTable/:restaurantId', getRandomTableController);
+}
-router.get("/tables/:tableId", getTable);
+router.get('/tables/:tableId', getTableController);
+router.get('/tableStatus/:tableId', getTableStatusController);
-router.get("/tabs/:tabId", getTab);
+router.get('/tabs/:tabId', getTabController);
-router.post("/orders/new/:tabId", newOrder);
-router.get("/orders/:orderId", getOrder);
+router.post('/orders/new/:tabId', newOrderController);
+router.get('/orders/:orderId', getOrderController);
-router.get("/actions/callWaiter/:tabId", callWaiter);
-router.get("/actions/payCash/:tabId", payCash);
-router.get("/actions/payCard/:tabId", payCard);
+router.get('/actions/callWaiter/:tabId', callWaiterController);
+router.get('/actions/payCash/:tabId', payCardController);
+router.get('/actions/payCard/:tabId', payCashController);
module.exports = router;
diff --git a/backend/app/utils/helperFuntions.js b/backend/app/utils/helperFuntions.js
deleted file mode 100644
index 0a453cf..0000000
--- a/backend/app/utils/helperFuntions.js
+++ /dev/null
@@ -1,28 +0,0 @@
-const { MongoClient } = require("mongodb");
-const { Tabs, Restaurants } = require("../db/models");
-
-async function closeTab(tabId) {
- try {
- const now = Date.now();
- const tab = await Tabs.findById(tabId);
-
- if (tab.status !== 'closed') {
- tab.status = 'closed';
- tab.lastUpdated = now;
- await tab.save();
-
- const table = await Restaurants.findById(tab.restaurantId);
- table.olderTabs.push(tab._id);
- table.tabOpen = false;
- table.lastUpdated = now;
- table.currentTab = null;
- await table.save();
- return true;
- } else return false;
- } catch (e) {
- console.log(e);
- return false;
- }
-}
-
-module.exports = { closeTab };
diff --git a/backend/index.js b/backend/index.js
index 98d8116..1a9ecde 100644
--- a/backend/index.js
+++ b/backend/index.js
@@ -1,62 +1,69 @@
-const fs = require("fs");
-const https = require("https");
-const http = require("http");
+const https = require('https');
+const http = require('http');
-const socketIo = require("socket.io");
-const { connectToDatabase, dbUrl } = require("./app/db/connection");
-const { createApp, createCollectionWatcher, createSocketHandler } = require("./app");
+const socketIo = require('socket.io');
+const { connectToDatabase } = require('./app/db/connection');
+const { createApp, createCollectionWatcher, createSocketHandler } = require('./app');
const app = createApp();
-const PROD = process.env.NODE_ENV === "production";
-const options = {};
+const PROD = process.env.NODE_ENV === 'production';
-if (PROD) {
- if (!process.env.KEY || !process.env.CERT) {
- console.error("Missing KEY or CERT environment variables");
- console.error("Exiting...");
- process.exit(1);
- } else {
- options.key = fs.readFileSync(process.env.KEY);
- options.cert = fs.readFileSync(process.env.CERT);
- }
+if (PROD && (!process.env.KEY || !process.env.CERT)) {
+ console.error('Missing KEY or CERT environment variables');
+ console.error('Exiting...');
+ process.exit(1);
}
-let origins = require("./cors.json").origins;
+
+let origins = require('./cors.json').origins;
setInterval(() => {
- try {
- delete require.cache[require.resolve("./cors.json")];
- origins = require("./cors.json").origins;
- } catch (e) {
- console.log("Socket.io: Error reading cors.json file");
- }
+ try {
+ delete require.cache[require.resolve('./cors.json')];
+ origins = require('./cors.json').origins;
+ } catch (e) {
+ console.log('Socket.io: Error reading cors.json file');
+ }
}, 60000);
-const server = PROD ? https.createServer(options, app) : http.createServer(app);
+const server = PROD ? https.createServer(app) : http.createServer(app);
+
+if (PROD) {
+ server.addContext('tabley.app', {
+ key: process.env.KEY,
+ cert: process.env.CERT
+ });
+
+ server.addContext('order.rocknrolla.ro', {
+ key: process.env.ROCKKEY,
+ cert: process.env.ROCKCERT
+ });
+}
const io = socketIo(server, {
- cors: {
- origin: function (origin, callback) {
- if (!origin) return callback(null, true);
- if (origins.indexOf(origin) !== -1) {
- callback(null, true);
- } else {
- console.log("Socket.io: Blocked by CORS: " + origin);
- callback('Not allowed by CORS');
- }
- }
- }
+ cors: {
+ origin: function (origin, callback) {
+ if (!origin) return callback(null, true);
+ if (origins.indexOf(origin) !== -1) {
+ callback(null, true);
+ } else {
+ console.log('Socket.io: Blocked by CORS: ' + origin);
+ callback('Not allowed by CORS');
+ }
+ }
+ }
});
createSocketHandler(io);
const PORT = PROD ? 443 : process.env.PORT || 3000;
server.listen(PORT, async () => {
- try {
- console.log("Connecting to MongoDB...");
- await connectToDatabase();
- await createCollectionWatcher(io);
- console.log("Connection established to MongoDB...");
- console.log(`Listening on port ${PORT}..`);
- } catch (error) {
- console.log("Could not connect to MongoDB...", error);
- }
-});
+ try {
+ console.log('Connecting to MongoDB...');
+ await connectToDatabase();
+ await createCollectionWatcher(io);
+ console.log('Connection established to MongoDB...');
+ console.log(`Listening on port ${PORT}..`);
+
+ } catch (error) {
+ console.log('Could not connect to MongoDB...', error);
+ }
+});
\ No newline at end of file
diff --git a/backend/package.json b/backend/package.json
index 2d65988..07af959 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -15,18 +15,24 @@
"dependencies": {
"compression": "^1.7.4",
"cors": "^2.8.5",
+ "eslint-config-prettier": "^8.8.0",
+ "eslint-plugin-prettier": "^4.2.1",
"express": "^4.17.1",
+ "express-basic-auth": "^1.2.1",
"helmet": "^4.4.1",
"https": "^1.0.0",
"jest": "^29.5.0",
"mongodb": "^5.3.0",
"mongoose": "^5.11.14",
"morgan": "^1.10.0",
+ "prettier": "^2.8.8",
+ "prometheus-remote-write": "^0.3.0",
"sinon": "^15.0.3",
"socket.io": "^4.6.1",
"supertest": "^6.3.3"
},
"devDependencies": {
+ "eslint": "^8.42.0",
"nodemon": "^2.0.22"
}
}
diff --git a/backend/yarn.lock b/backend/yarn.lock
index 433147f..f736113 100644
--- a/backend/yarn.lock
+++ b/backend/yarn.lock
@@ -299,6 +299,57 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
+"@eslint-community/eslint-utils@^4.2.0":
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59"
+ integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==
+ dependencies:
+ eslint-visitor-keys "^3.3.0"
+
+"@eslint-community/regexpp@^4.4.0":
+ version "4.5.1"
+ resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.1.tgz#cdd35dce4fa1a89a4fd42b1599eb35b3af408884"
+ integrity sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==
+
+"@eslint/eslintrc@^2.0.3":
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.3.tgz#4910db5505f4d503f27774bf356e3704818a0331"
+ integrity sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.5.2"
+ globals "^13.19.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.1.2"
+ strip-json-comments "^3.1.1"
+
+"@eslint/js@8.42.0":
+ version "8.42.0"
+ resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.42.0.tgz#484a1d638de2911e6f5a30c12f49c7e4a3270fb6"
+ integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==
+
+"@humanwhocodes/config-array@^0.11.10":
+ version "0.11.10"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.10.tgz#5a3ffe32cc9306365fb3fd572596cd602d5e12d2"
+ integrity sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.5"
+
+"@humanwhocodes/module-importer@^1.0.1":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c"
+ integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
"@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced"
@@ -544,6 +595,80 @@
"@jridgewell/resolve-uri" "3.1.0"
"@jridgewell/sourcemap-codec" "1.4.14"
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.8":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
+ integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==
+
+"@protobufjs/base64@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735"
+ integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==
+
+"@protobufjs/codegen@^2.0.4":
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb"
+ integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==
+
+"@protobufjs/eventemitter@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70"
+ integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==
+
+"@protobufjs/fetch@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45"
+ integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==
+ dependencies:
+ "@protobufjs/aspromise" "^1.1.1"
+ "@protobufjs/inquire" "^1.1.0"
+
+"@protobufjs/float@^1.0.2":
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1"
+ integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==
+
+"@protobufjs/inquire@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
+ integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
+
+"@protobufjs/path@^1.1.2":
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d"
+ integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==
+
+"@protobufjs/pool@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54"
+ integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==
+
+"@protobufjs/utf8@^1.1.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570"
+ integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
+
"@sinclair/typebox@^0.25.16":
version "0.25.24"
resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.25.24.tgz#8c7688559979f7079aacaf31aa881c3aa410b718"
@@ -674,6 +799,11 @@
dependencies:
"@types/istanbul-lib-report" "*"
+"@types/long@^4.0.1":
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
+ integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==
+
"@types/mongodb@^3.5.27":
version "3.6.20"
resolved "https://registry.yarnpkg.com/@types/mongodb/-/mongodb-3.6.20.tgz#b7c5c580644f6364002b649af1c06c3c0454e1d2"
@@ -692,6 +822,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f"
integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==
+"@types/node@>=13.7.0":
+ version "20.3.1"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-20.3.1.tgz#e8a83f1aa8b649377bb1fb5d7bac5cb90e784dfe"
+ integrity sha512-EhcH/wvidPy1WeML3TtYFGR83UzjxeWRen9V402T8aUGYsCHOmfoisV3ZSg03gAFIbLq8TnWOJ0f4cALtnSEUg==
+
"@types/prettier@^2.1.5":
version "2.7.2"
resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.2.tgz#6c2324641cc4ba050a8c710b2b251b377581fbf0"
@@ -740,6 +875,26 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
mime-types "~2.1.34"
negotiator "0.6.3"
+acorn-jsx@^5.3.2:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^8.8.0:
+ version "8.8.2"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.2.tgz#1b2f25db02af965399b9776b0c2c391276d37c4a"
+ integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==
+
+ajv@^6.10.0, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
ansi-escapes@^4.2.1:
version "4.3.2"
resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
@@ -786,6 +941,11 @@ argparse@^1.0.7:
dependencies:
sprintf-js "~1.0.2"
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
array-flatten@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
@@ -871,7 +1031,7 @@ base64id@2.0.0, base64id@~2.0.0:
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==
-basic-auth@~2.0.1:
+basic-auth@^2.0.1, basic-auth@~2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
@@ -1186,7 +1346,7 @@ cors@^2.8.5, cors@~2.8.5:
object-assign "^4"
vary "^1"
-cross-spawn@^7.0.3:
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@@ -1216,7 +1376,7 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
-debug@^4.1.0, debug@^4.1.1, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
+debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
@@ -1228,6 +1388,11 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
deepmerge@^4.2.2:
version "4.3.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
@@ -1276,6 +1441,13 @@ diff@^5.1.0:
resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40"
integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
ee-first@1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
@@ -1349,11 +1521,119 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-config-prettier@^8.8.0:
+ version "8.8.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz#bfda738d412adc917fd7b038857110efe98c9348"
+ integrity sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==
+
+eslint-plugin-prettier@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b"
+ integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
+eslint-scope@^7.2.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.0.tgz#f21ebdafda02352f103634b96dd47d9f81ca117b"
+ integrity sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1:
+ version "3.4.1"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994"
+ integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==
+
+eslint@^8.42.0:
+ version "8.42.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.42.0.tgz#7bebdc3a55f9ed7167251fe7259f75219cade291"
+ integrity sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.2.0"
+ "@eslint-community/regexpp" "^4.4.0"
+ "@eslint/eslintrc" "^2.0.3"
+ "@eslint/js" "8.42.0"
+ "@humanwhocodes/config-array" "^0.11.10"
+ "@humanwhocodes/module-importer" "^1.0.1"
+ "@nodelib/fs.walk" "^1.2.8"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.2.0"
+ eslint-visitor-keys "^3.4.1"
+ espree "^9.5.2"
+ esquery "^1.4.2"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ find-up "^5.0.0"
+ glob-parent "^6.0.2"
+ globals "^13.19.0"
+ graphemer "^1.4.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ is-path-inside "^3.0.3"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.1.2"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+
+espree@^9.5.2:
+ version "9.5.2"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.2.tgz#e994e7dc33a082a7a82dceaf12883a829353215b"
+ integrity sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==
+ dependencies:
+ acorn "^8.8.0"
+ acorn-jsx "^5.3.2"
+ eslint-visitor-keys "^3.4.1"
+
esprima@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
+esquery@^1.4.2:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.5.0.tgz#6ce17738de8577694edd7361c57182ac8cb0db0b"
+ integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
etag@~1.8.1:
version "1.8.1"
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
@@ -1390,6 +1670,13 @@ expect@^29.5.0:
jest-message-util "^29.5.0"
jest-util "^29.5.0"
+express-basic-auth@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/express-basic-auth/-/express-basic-auth-1.2.1.tgz#d31241c03a915dd55db7e5285573049cfcc36381"
+ integrity sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==
+ dependencies:
+ basic-auth "^2.0.1"
+
express@^4.17.1:
version "4.18.2"
resolved "https://registry.yarnpkg.com/express/-/express-4.18.2.tgz#3fabe08296e930c796c19e3c516979386ba9fd59"
@@ -1427,16 +1714,38 @@ express@^4.17.1:
utils-merge "1.0.1"
vary "~1.1.2"
-fast-json-stable-stringify@^2.1.0:
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-diff@^1.1.2:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.3.0.tgz#ece407fa550a64d638536cd727e129c61616e0f0"
+ integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==
+
+fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
+
fast-safe-stringify@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz#c406a83b6e70d9e35ce3b30a81141df30aeba884"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
+fastq@^1.6.0:
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
+ integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
+ dependencies:
+ reusify "^1.0.4"
+
fb-watchman@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.2.tgz#e9524ee6b5c77e9e5001af0f85f3adbb8623255c"
@@ -1444,6 +1753,13 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
fill-range@^7.0.1:
version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@@ -1472,6 +1788,27 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
+find-up@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
+ integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
+ dependencies:
+ locate-path "^6.0.0"
+ path-exists "^4.0.0"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
+ integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
+
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
@@ -1545,6 +1882,13 @@ get-stream@^6.0.0:
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
+glob-parent@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
glob-parent@~5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
@@ -1569,11 +1913,23 @@ globals@^11.1.0:
resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
+globals@^13.19.0:
+ version "13.20.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.20.0.tgz#ea276a1e508ffd4f1612888f9d1bad1e2717bf82"
+ integrity sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==
+ dependencies:
+ type-fest "^0.20.2"
+
graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+graphemer@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
+ integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
@@ -1644,6 +2000,19 @@ ignore-by-default@^1.0.1:
resolved "https://registry.yarnpkg.com/ignore-by-default/-/ignore-by-default-1.0.1.tgz#48ca6d72f6c6a3af00a9ad4ae6876be3889e2b09"
integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==
+ignore@^5.2.0:
+ version "5.2.4"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324"
+ integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
import-local@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
@@ -1714,7 +2083,7 @@ is-generator-fn@^2.0.0:
resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118"
integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==
-is-glob@^4.0.1, is-glob@~4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@@ -1726,6 +2095,11 @@ is-number@^7.0.0:
resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+is-path-inside@^3.0.3:
+ version "3.0.3"
+ resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283"
+ integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==
+
is-stream@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
@@ -2162,6 +2536,13 @@ js-yaml@^3.13.1:
argparse "^1.0.7"
esprima "^4.0.0"
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@@ -2172,6 +2553,16 @@ json-parse-even-better-errors@^2.3.0:
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
+
json5@^2.2.2:
version "2.2.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
@@ -2197,6 +2588,14 @@ leven@^3.1.0:
resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2"
integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
lines-and-columns@^1.1.6:
version "1.2.4"
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
@@ -2209,11 +2608,28 @@ locate-path@^5.0.0:
dependencies:
p-locate "^4.1.0"
+locate-path@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286"
+ integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==
+ dependencies:
+ p-locate "^5.0.0"
+
lodash.get@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+long@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28"
+ integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==
+
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
@@ -2302,7 +2718,7 @@ mimic-fn@^2.1.0:
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
-minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -2529,6 +2945,18 @@ optional-require@^1.1.8:
dependencies:
require-at "^1.0.6"
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
p-limit@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
@@ -2536,7 +2964,7 @@ p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
-p-limit@^3.1.0:
+p-limit@^3.0.2, p-limit@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
@@ -2550,11 +2978,25 @@ p-locate@^4.1.0:
dependencies:
p-limit "^2.2.0"
+p-locate@^5.0.0:
+ version "5.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834"
+ integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==
+ dependencies:
+ p-limit "^3.0.2"
+
p-try@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6"
integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
parse-json@^5.2.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd"
@@ -2624,6 +3066,23 @@ pkg-dir@^4.2.0:
dependencies:
find-up "^4.0.0"
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@^2.8.8:
+ version "2.8.8"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.8.tgz#e8c5d7e98a4305ffe3de2e1fc4aca1a71c28b1da"
+ integrity sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==
+
pretty-format@^29.5.0:
version "29.5.0"
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.5.0.tgz#283134e74f70e2e3e7229336de0e4fce94ccde5a"
@@ -2638,6 +3097,14 @@ process-nextick-args@~2.0.0:
resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+prometheus-remote-write@^0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/prometheus-remote-write/-/prometheus-remote-write-0.3.0.tgz#35159407c4f2c9708a6fa526153781bdde484cf2"
+ integrity sha512-9xfJAGYRpmbZZCwUZzzRtJwOianNguDwv2hUF2aWDqhFUDdE6HDjPDw4tVDiRK+mnC+b53tJBCjxVBgSbMD05Q==
+ dependencies:
+ protobufjs "^6.11.3"
+ snappyjs "^0.6.1"
+
prompts@^2.0.1:
version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
@@ -2646,6 +3113,25 @@ prompts@^2.0.1:
kleur "^3.0.3"
sisteransi "^1.0.5"
+protobufjs@^6.11.3:
+ version "6.11.3"
+ resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74"
+ integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==
+ dependencies:
+ "@protobufjs/aspromise" "^1.1.2"
+ "@protobufjs/base64" "^1.1.2"
+ "@protobufjs/codegen" "^2.0.4"
+ "@protobufjs/eventemitter" "^1.1.0"
+ "@protobufjs/fetch" "^1.1.0"
+ "@protobufjs/float" "^1.0.2"
+ "@protobufjs/inquire" "^1.1.0"
+ "@protobufjs/path" "^1.1.2"
+ "@protobufjs/pool" "^1.1.0"
+ "@protobufjs/utf8" "^1.1.0"
+ "@types/long" "^4.0.1"
+ "@types/node" ">=13.7.0"
+ long "^4.0.0"
+
proxy-addr@~2.0.7:
version "2.0.7"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
@@ -2659,7 +3145,7 @@ pstree.remy@^1.1.8:
resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a"
integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==
-punycode@^2.1.1:
+punycode@^2.1.0, punycode@^2.1.1:
version "2.3.0"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
@@ -2683,6 +3169,11 @@ qs@^6.11.0:
dependencies:
side-channel "^1.0.4"
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
range-parser@~1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
@@ -2745,6 +3236,11 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
@@ -2764,6 +3260,25 @@ resolve@^1.20.0:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
@@ -2912,6 +3427,11 @@ smart-buffer@^4.2.0:
resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae"
integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==
+snappyjs@^0.6.1:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/snappyjs/-/snappyjs-0.6.1.tgz#9bca9ff8c54b133a9cc84a71d22779e97fc51878"
+ integrity sha512-YIK6I2lsH072UE0aOFxxY1dPDCS43I5ktqHpeAsuLNYWkE5pGxRGWfDM4/vSUfNzXjC1Ivzt3qx31PCLmc9yqg==
+
socket.io-adapter@~2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12"
@@ -3025,7 +3545,7 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
-strip-json-comments@^3.1.1:
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
version "3.1.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
@@ -3089,6 +3609,11 @@ test-exclude@^6.0.0:
glob "^7.1.4"
minimatch "^3.0.4"
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
+
tmpl@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc"
@@ -3125,11 +3650,23 @@ tr46@^3.0.0:
dependencies:
punycode "^2.1.1"
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
type-detect@4.0.8, type-detect@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
type-fest@^0.21.3:
version "0.21.3"
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
@@ -3161,6 +3698,13 @@ update-browserslist-db@^1.0.10:
escalade "^3.1.1"
picocolors "^1.0.0"
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -3212,6 +3756,11 @@ which@^2.0.1:
dependencies:
isexe "^2.0.0"
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
diff --git a/frontend/src/App.js b/frontend/src/App.js
index 7cb7fa6..809bd68 100644
--- a/frontend/src/App.js
+++ b/frontend/src/App.js
@@ -33,46 +33,63 @@ export default function App() {
onClose: cartOnClose,
} = useDisclose();
+ const setRestaurantDetails = async ({
+ restaurantName,
+ restaurantHostname,
+ }) => {
+ if (restaurantName) {
+ const restaurant = await state.api.getRestaurantByName(restaurantName);
+ if (restaurant) {
+ state.restaurantId = restaurant.restaurantId;
+ state.restaurantName = restaurant.restaurantName;
+ state.products = await state.api.getMenu(restaurant.restaurantId);
+ } else {
+ alert(
+ "2. Sorry but the url is not valid. Please scan a QR code or use the link provided by the restaurant."
+ );
+ }
+ } else if (restaurantHostname) {
+ const restaurant = await state.api.getRestaurantByHostname(
+ restaurantHostname
+ );
+ if (restaurant) {
+ state.restaurantId = restaurant.restaurantId;
+ state.restaurantName = restaurant.restaurantName;
+ state.products = await state.api.getMenu(restaurant.restaurantId);
+ } else {
+ alert(
+ "3. Sorry but the url is not valid. Please scan a QR code or use the link provided by the restaurant."
+ );
+ }
+ }
+ };
+
useEffect(() => {
(async () => {
state.api = new Api((msg) => toastEmitter.emit("showToast", msg));
try {
const url = new URL(window.location);
- if (url.hostname === "localhost") state.developerMode = true;
+ // if (url.hostname === "localhost") state.developerMode = true;
+
if (url.hostname === "tabley.app" || url.hostname === "localhost") {
- if (!url.pathname.startsWith("/@")) {
- alert(
- "1. Sorry but you can't access this page directly. Please scan a QR code or use the link provided by the restaurant."
- );
- } else {
+ if (
+ url.pathname.startsWith("/@") &&
+ url.pathname.endsWith("/restaurant-view")
+ ) {
+ let restaurantName = url.pathname
+ .replace("/restaurant-view", "")
+ .toLowerCase();
+ restaurantName = restaurantName.replace("/@", "");
+ await setRestaurantDetails({ restaurantName });
+ state.user = "owner";
+ } else if (url.pathname.startsWith("/@")) {
const restaurantName = url.pathname.replace("/@", "").toLowerCase();
-
- const restaurant = await state.api.getRestaurantByName(
- restaurantName
- );
- if (restaurant) {
- state.restaurantId = restaurant.restaurantId;
- state.restaurantName = restaurant.restaurantName;
- state.products = await state.api.getMenu(restaurant.restaurantId);
- } else {
- alert(
- "2. Sorry but the url is not valid. Please scan a QR code or use the link provided by the restaurant."
- );
- }
+ await setRestaurantDetails({ restaurantName });
}
} else {
- const restaurant = await state.api.getRestaurantByHostname(
- url.hostname
- );
- if (restaurant) {
- state.restaurantId = restaurant.restaurantId;
- state.restaurantName = restaurant.restaurantName;
- state.products = await state.api.getMenu(restaurant.restaurantId);
- } else {
- alert(
- "3. Sorry but the url is not valid. Please scan a QR code or use the link provided by the restaurant."
- );
- }
+ await setRestaurantDetails({
+ restaurantHostname: url.hostname,
+ });
}
} catch (e) {
alert(
@@ -110,7 +127,7 @@ export default function App() {
)}
{user === "owner" && }
- {developerMode && user === "customer" && (
+ {developerMode && user === "customer" && currentView === "scan" && (
)}
diff --git a/frontend/src/components/Order.js b/frontend/src/components/Order.js
index 2660dfd..82e5ba1 100644
--- a/frontend/src/components/Order.js
+++ b/frontend/src/components/Order.js
@@ -1,3 +1,5 @@
+/* eslint-disable no-plusplus */
+/* eslint-disable no-restricted-syntax */
/* eslint-disable no-nested-ternary */
/* eslint-disable react/prop-types */
import {
@@ -15,8 +17,56 @@ import React from "react";
import Time from "./Time";
+const composeItemsFromVariations = (stateOrder) => {
+ const order = JSON.parse(JSON.stringify(stateOrder));
+ const { items, variations } = order;
+ const updatedItems = [];
+
+ // Iterate over the variations
+ for (const variant of variations) {
+ // Iterate over the quantity
+ while (variant.qty > 0) {
+ const item = items.find((i) => i._id === variant.id);
+ const newItem = JSON.parse(JSON.stringify(item));
+ // Move the item so we don't loop over it again
+ const itemIndex = items.indexOf(item);
+ order.items.splice(itemIndex, 1);
+
+ // Update the recipe and quantity
+ newItem.recipe = newItem.recipe.filter(
+ (recipe) => recipe._id === variant.recipe
+ );
+ newItem.cartQty = 1;
+ variant.qty -= 1;
+
+ // Add the updated item to the result
+ updatedItems.push(newItem);
+ }
+ }
+
+ // Add the new items to the order
+ order.items.push(...updatedItems);
+
+ order.items = order.items.reduce((acc, item) => {
+ const existingItem = acc.find((i) =>
+ i.type === "product"
+ ? i._id === item._id
+ : i._id === item._id && i.recipe[0]._id === item.recipe[0]._id
+ );
+ if (existingItem) {
+ existingItem.cartQty += 1;
+ return acc;
+ }
+ // eslint-disable-next-line no-param-reassign
+ item.cartQty = 1;
+ return acc.concat(item);
+ }, []);
+
+ return order;
+};
+
function Order({ order, orderNr }) {
- const mutableItems = JSON.parse(JSON.stringify(order.items));
+ const mutableItems = composeItemsFromVariations(order).items;
return (
@@ -55,34 +105,68 @@ function Order({ order, orderNr }) {
{`Order #${orderNr}`}
- {mutableItems
- .reduce((acc, item) => {
- const existingItem = acc.find((i) => i._id === item._id);
- if (existingItem) {
- existingItem.qty += 1;
- return acc;
- }
- // eslint-disable-next-line no-param-reassign
- item.qty = 1;
- return acc.concat(item);
- }, [])
- .map((orderedItem) =>
- orderedItem.type === "product" ? (
+ {mutableItems.map((orderedItem) =>
+ orderedItem.type === "product" ? (
+
+
+ {orderedItem.cartQty}x{" "}
+
+
+ {orderedItem.name}
+
+
+ {orderedItem.price * orderedItem.cartQty} RON
+
+
+ ) : (
+
+
+ {orderedItem.cartQty}x{" "}
+
- {orderedItem.qty}x {orderedItem.name}
+ {orderedItem.name}
-
- {orderedItem.price * orderedItem.qty} RON
+
+ -{orderedItem.recipe[0].name}
- ) : null
- )}
+
+ {orderedItem.recipe[0].price * orderedItem.cartQty} RON
+
+
+ )
+ )}
diff --git a/frontend/src/components/Toast.js b/frontend/src/components/Toast.js
index e174dcc..2ee2635 100644
--- a/frontend/src/components/Toast.js
+++ b/frontend/src/components/Toast.js
@@ -48,11 +48,10 @@ export function ToastManager() {
fontSize="md"
fontWeight="medium"
flexShrink={1}
- color="darkText"
>
{title}
-
+
{description}
diff --git a/frontend/src/settings.js b/frontend/src/settings.js
index 0f6bf2e..e9ad667 100644
--- a/frontend/src/settings.js
+++ b/frontend/src/settings.js
@@ -1,6 +1,6 @@
export const baseUrl =
window.location.hostname === "localhost"
? "http://localhost/api"
- : `http://${window.location.hostname}/api`;
+ : `https://${window.location.hostname}/api`;
export const version = "0.0.1";
diff --git a/frontend/src/state/index.js b/frontend/src/state/index.js
index fede387..01c1f59 100644
--- a/frontend/src/state/index.js
+++ b/frontend/src/state/index.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-restricted-syntax */
/* eslint-disable array-callback-return */
/* eslint-disable no-param-reassign */
/* eslint-disable react-hooks/rules-of-hooks */
@@ -9,6 +10,8 @@ const hstate = hookstate(
restaurantId: null,
restaurantName: "Welcome to Tabley!",
+ isExternal: false,
+
user: "customer",
viewHistory: ["scan"],
@@ -38,6 +41,7 @@ const hstate = hookstate(
subCategories: [],
openOrders: [],
+ openOrdersPerTable: [],
tables: [],
ActionView: "tab",
@@ -66,10 +70,108 @@ const extractCategoriesAndSubCategories = (products) => {
};
};
+const transformOpenOrdersToOpenOrdersPerTable = (openOrders) => {
+ // these are raw orders, not hookstate objects
+ openOrders.forEach((order, oIndex) => {
+ // filter out the orders that are not 'recieved' aka open
+ if (order.status !== "recieved") {
+ openOrders.splice(oIndex, 1);
+ } else {
+ // Modify the items to only include the recipe that was ordered
+ for (const variant of order.variations) {
+ // Iterate over the quantity
+ while (variant.qty > 0) {
+ // Find the item in the order that matches the variant
+ const item = order.items.find((i) => i._id === variant.id);
+ // if the item was deleted, skip it
+ if (item) {
+ // Create a copy of the item
+ const newItem = JSON.parse(JSON.stringify(item));
+ // Delete the item from the order so we don't loop over it again
+ const itemIndex = order.items.indexOf(item);
+ order.items.splice(itemIndex, 1);
+ // Update the recipe and quantity
+ newItem.recipe = newItem.recipe.filter(
+ (recipe) => recipe._id === variant.recipe
+ );
+ newItem.cartQty = 1;
+ // Update the quantity of the variant
+ variant.qty -= 1;
+ // Add the updated item to the result
+ order.items.push(newItem);
+ }
+ }
+ }
+ // Reduce the items to only include the unique items and their quantity
+ // order.items.forEach((item, iIndex) => {
+ // const existingItem = order.items.find((i, index) => {
+ // if (i.type === "product") {
+ // if (i._id === item._id) {
+ // order.items.splice(index, 1);
+ // return true;
+ // }
+ // }
+ // if (i.type === "variation") {
+ // if (i._id === item._id && i.recipe[0]._id === item.recipe[0]._id) {
+ // order.items.splice(index, 1);
+ // return true;
+ // }
+ // }
+ // return false;
+ // });
+ // if (existingItem) {
+ // console.log(existingItem);
+ // if (!item.cartQty) {
+ // item.cartQty = 1;
+ // } else {
+ // item.cartQty += 1;
+ // }
+ // }
+ // });
+
+ order.items = order.items.reduce((acc, item) => {
+ const existingItemIndex = acc.findIndex((i) => {
+ if (i.type === "product") {
+ return i._id === item._id;
+ }
+ if (i.type === "variation") {
+ return i._id === item._id && i.recipe[0]._id === item.recipe[0]._id;
+ }
+ return false;
+ });
+
+ if (existingItemIndex !== -1) {
+ if (!acc[existingItemIndex].cartQty) {
+ acc[existingItemIndex].cartQty = 1;
+ } else {
+ acc[existingItemIndex].cartQty += 1;
+ }
+ } else {
+ if (!item.cartQty) {
+ item.cartQty = 1;
+ }
+ acc.push(item);
+ }
+
+ return acc;
+ }, []);
+ }
+ });
+ console.log(openOrders);
+ return openOrders;
+};
+
export function globalState() {
const ustate = useHookstate(hstate);
return {
+ get isExternal() {
+ return ustate.isExternal.get();
+ },
+ set isExternal(bool) {
+ ustate.isExternal.set(bool);
+ },
+
get developerMode() {
return ustate.developerMode.get();
},
@@ -418,15 +520,26 @@ export function globalState() {
ustate.viewHistory.set(viewHistory);
},
+ get openOrdersPerTable() {
+ return ustate.openOrdersPerTable.get();
+ },
+
get openOrders() {
return ustate.openOrders.get();
},
+
set openOrders(orders) {
ustate.openOrders.set(orders);
+ ustate.openOrdersPerTable.set(
+ transformOpenOrdersToOpenOrdersPerTable(orders)
+ );
},
+
pushNewOrder(order) {
ustate.openOrders.set((prev) => [order, ...prev]);
+ // update openOrdersPerTable
},
+
updateOpenOrderStatus(orderId, status) {
const openOrders = ustate.openOrders.get();
const openOrderIndex = openOrders.findIndex(
@@ -439,6 +552,7 @@ export function globalState() {
// If the status is not 'canceled', just update the status
ustate.openOrders[openOrderIndex].status.set(status);
}
+ // update openOrdersPerTable
},
get restaurantId() {
diff --git a/frontend/src/views/Categories.js b/frontend/src/views/Categories.js
index 95c1741..8d33089 100644
--- a/frontend/src/views/Categories.js
+++ b/frontend/src/views/Categories.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-nested-ternary */
import {
View,
VStack,
@@ -13,6 +14,20 @@ import { globalState } from "../state";
const Categories = () => {
const state = globalState();
+ const { categories } = state;
+
+ const sortedCategories = JSON.parse(JSON.stringify(categories)).sort(
+ (a, b) => {
+ if (a.name < b.name) {
+ return -1;
+ }
+ if (a.name > b.name) {
+ return 1;
+ }
+
+ return 0;
+ }
+ );
return [
@@ -27,7 +42,7 @@ const Categories = () => {
flexWrap="wrap"
paddingX="1rem"
>
- {state.categories.map((category) => (
+ {sortedCategories.map((category) => (
{
state.category = category;
diff --git a/frontend/src/views/Owner/OpenOrders.js b/frontend/src/views/Owner/OpenOrders.js
index 3c96a8d..30934a4 100644
--- a/frontend/src/views/Owner/OpenOrders.js
+++ b/frontend/src/views/Owner/OpenOrders.js
@@ -1,5 +1,5 @@
import { View, HStack, Text } from "native-base";
-import React, { useEffect, useState } from "react";
+import React, { useEffect, useState, useMemo } from "react";
import OpenOrder from "./components/OpenOrder";
import { toastEmitter } from "../../components/Toast";
@@ -23,35 +23,33 @@ const changeStatusHandler = async (orderId, status) => {
};
function OpenOrders() {
- const [groupedOrders, setGroupedOrders] = useState({});
+ const [groupedOrders, setGroupedOrders] = useState([]);
const state = globalState();
const { openOrders, selectedTable } = state;
- // openOrders.forEach((order) => {
- // const tableId = order.tabId.tableId._id;
- // const localGroupedOrders = {};
- // if (groupedOrders[tableId]) {
- // localGroupedOrders[tableId].push(order);
- // } else {
- // localGroupedOrders[tableId] = [order];
- // }
- // setGroupedOrders({ ...groupedOrders, ...localGroupedOrders });
- // });
-
- // const sortedOrders = Object.values(groupedOrders).flatMap((orders) =>
- // orders.sort((a, b) =>
- // a.tabId.tableId._id.localeCompare(b.tabId.tableId._id)
- // )
- // );
-
- useEffect(() => {
+ useMemo(() => {
if (selectedTable) {
- openOrders.filter(
- (order) => order.tabId.tableId._id === selectedTable._id
+ setGroupedOrders(
+ openOrders.filter(
+ (order) => order.tabId.tableId._id === selectedTable._id
+ )
);
}
- }, [selectedTable]);
+ }, [state.selectedTable?.value]);
+
+ useEffect(() => {
+ if (openOrders.length > 0 && !selectedTable) {
+ setGroupedOrders(openOrders);
+ }
+ }, [openOrders]);
+
+ useEffect(
+ () => () => {
+ state.selectedTable = null;
+ },
+ []
+ );
return (
@@ -59,7 +57,7 @@ function OpenOrders() {
Open Orders {selectedTable && `for Table #${selectedTable.tableNo}`}
- {openOrders.map((order, index) => (
+ {groupedOrders.map((order, index) => (
import("./components/Table"));
+
function Tables() {
const state = globalState();
const { tables } = state;
+ // useEffect(() => {
+ // if (state.openOrdersPerTable) {
+ // console.log(state.openOrdersPerTable);
+ // }
+ // }, [state.openOrdersPerTable]);
+
return (
@@ -17,14 +24,16 @@ function Tables() {
{tables.map(
(table) =>
table.currentTab && (
-
+ Loading...}>
+
+
)
)}
@@ -37,14 +46,16 @@ function Tables() {
{tables.map(
(table) =>
!table.currentTab && (
-
+ Loading...}>
+
+
)
)}
diff --git a/frontend/src/views/Owner/components/Table.js b/frontend/src/views/Owner/components/Table.js
index 1d8108e..f12b204 100644
--- a/frontend/src/views/Owner/components/Table.js
+++ b/frontend/src/views/Owner/components/Table.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-restricted-syntax */
/* eslint-disable no-else-return */
/* eslint-disable no-param-reassign */
/* eslint-disable react/jsx-props-no-spreading */
@@ -12,80 +13,80 @@ import {
HStack,
Pressable,
} from "native-base";
-import React, { useEffect, useState } from "react";
+import React, { useMemo, useState, Suspense } from "react";
-import OccupiedTime from "./OccupiedTime";
import { globalState } from "../../../state";
-const activeOrders = (orders) =>
- orders.reduce((accumulator, order) => {
- if (order.status === "recieved") accumulator.push(order);
- return accumulator;
- }, []);
-
-const flattenOrderItems = (orders) => orders.flatMap((order) => order.items);
-const flattenVariations = (orders) =>
- orders.flatMap((order) => order.variations);
-
-const removeRecipesBasedOnVariations = (items, variations) =>
- items.map((item) => {
- // loop through each variation and if item id === variation id,
- // remove all the recipes except the one that matches the variation
- // then substract 1 from variation.qty
- if (item.recipe.length === 0) {
- return item;
- }
-
- variations.forEach((variation) => {
- if (variation.qty === 0) return;
- const newRecipe = item.recipe.find((currentRecipe) => {
- if (currentRecipe._id === variation.recipe) {
- variation.qty -= 1;
- return true;
- }
- return false;
+const OccupiedTime = React.lazy(() => import("./OccupiedTime"));
+
+function Table({ table, ...props }) {
+ const activeOrders = (orders) =>
+ orders.reduce((accumulator, order) => {
+ if (order.status === "recieved") accumulator.push(order);
+ return accumulator;
+ }, []);
+ const flattenOrderItems = (orders) => orders.flatMap((order) => order.items);
+ const flattenVariations = (orders) =>
+ orders.flatMap((order) => order.variations);
+
+ const removeRecipesBasedOnVariations = (items, variations) =>
+ items.map((item) => {
+ // loop through each variation and if item id === variation id,
+ // remove all the recipes except the one that matches the variation
+ // then substract 1 from variation.qty
+ if (item.recipe.length === 0) {
+ return item;
+ }
+
+ variations.forEach((variation) => {
+ if (variation.qty === 0) return;
+ const newRecipe = item.recipe.find((currentRecipe) => {
+ if (currentRecipe._id === variation.recipe) {
+ variation.qty -= 1;
+ return true;
+ }
+ return false;
+ });
+ if (newRecipe) item.recipe = [newRecipe];
});
- if (newRecipe) item.recipe = [newRecipe];
+
+ return item;
});
- return item;
- });
-
-const groupItemsByIdAndRecipe = (items) =>
- items.reduce((acc, currentItem) => {
- // Create a key based on _id and recipe[0]._id (if it exists)
- const key =
- currentItem._id +
- (currentItem.recipe[0] ? currentItem.recipe[0]._id : "");
-
- // If the key already exists, increment qty, else create a new entry
- if (acc[key]) {
- acc[key].qty += 1;
- } else {
- acc[key] = {
- id: currentItem._id,
- qty: 1,
- name: currentItem.name,
- recipe: currentItem.recipe[0] || null,
- };
- }
-
- return acc;
- }, {});
+ const groupItemsByIdAndRecipe = (items) =>
+ items.reduce((acc, currentItem) => {
+ // Create a key based on _id and recipe[0]._id (if it exists)
+ const key =
+ currentItem._id +
+ (currentItem.recipe[0] ? currentItem.recipe[0]._id : "");
+
+ // If the key already exists, increment qty, else create a new entry
+ if (acc[key]) {
+ acc[key].qty += 1;
+ } else {
+ acc[key] = {
+ id: currentItem._id,
+ qty: 1,
+ name: currentItem.name,
+ recipe: currentItem.recipe[0] || null,
+ };
+ }
+
+ return acc;
+ }, {});
-function Table({ table, ...props }) {
const [tab, setTab] = useState(null);
const [groupedItems, setGroupedItems] = useState([]);
const state = globalState();
const { tableNo, currentTab } = table;
- useEffect(() => {
+ useMemo(() => {
if (!table.currentTab) return;
setTab(JSON.parse(JSON.stringify(table.currentTab)));
}, [table]);
- useEffect(() => {
+ useMemo(() => {
if (!tab) return;
const orders = activeOrders(tab.orders);
@@ -115,7 +116,9 @@ function Table({ table, ...props }) {
{currentTab ? (
-
+ Loading...}>
+
+
) : (
"No current tab"
)}
diff --git a/frontend/src/views/QrScan.js b/frontend/src/views/QrScan.js
index 10f2b5f..fed829f 100644
--- a/frontend/src/views/QrScan.js
+++ b/frontend/src/views/QrScan.js
@@ -39,6 +39,29 @@ function QrScan() {
(async () => {
const { status } = await BarCodeScanner.requestPermissionsAsync();
setHasPermission(status === "granted");
+
+ try {
+ const url = new URL(window.location);
+ if (url.pathname.startsWith("/join/")) {
+ const tableId = url.pathname.replace("/join/", "").toLowerCase();
+
+ const tableData = await state.api.getTableInfo(tableId);
+ if (tableData) {
+ state.tableId = tableId;
+ state.tableInfo = tableData;
+ goToTableHandler();
+ } else {
+ alert(
+ "1. Hmm, spooky! It looks like the QR code is not valid. Please try again or ask the restaurant for help."
+ );
+ }
+ }
+ } catch (e) {
+ console.log(e);
+ alert(
+ "2. Hmm, spooky! It looks like the QR code is not valid. Please try again or ask the restaurant for help."
+ );
+ }
})();
}, []);
@@ -67,6 +90,7 @@ function QrScan() {
}
}
} catch (e) {
+ console.log(e);
console.log("Not a url, trying to parse as table");
const tableData = await api.getTableInfo(data);
if (tableData) {
@@ -80,7 +104,7 @@ function QrScan() {
const goToTableHandler = async () => {
const socket = new Socket(
null,
- restaurantId,
+ state.restaurantId,
false,
state.tableInfo.currentTab._id
);
@@ -88,6 +112,7 @@ function QrScan() {
socket.on("order:new", (data) => {
console.log("new order on tab");
+ console.log(data.order);
state.addOrder(data.order);
});
diff --git a/frontend/src/views/Table.js b/frontend/src/views/Table.js
index 53db774..250edb4 100644
--- a/frontend/src/views/Table.js
+++ b/frontend/src/views/Table.js
@@ -17,7 +17,11 @@ const Table = () => {
// state.totalProducts ? : null,
state.orders.length
? state.orders.map((order, orderNr) => (
-
+
))
: null,
!state.orders.length && !state.totalProducts ? (