mirror of
				https://github.com/KevinMidboe/planetposen-backend.git
				synced 2025-10-29 16:30:13 +00:00 
			
		
		
		
	Webserver w/ api-routes, controllers, middleware & cart websocket server
This commit is contained in:
		
							
								
								
									
										44
									
								
								src/webserver/controllers/loginController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/webserver/controllers/loginController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| import cookie from "cookie"; | ||||
| import type { Request, Response, NextFunction } from "express"; | ||||
|  | ||||
| const cookieOptions = { | ||||
|   path: "/", | ||||
|   maxAge: 60 * 60 * 24 * 7, | ||||
| }; | ||||
|  | ||||
| function addAdminCookie(res: Response) { | ||||
|   const adminCookie = cookie.serialize("admin", true, cookieOptions); | ||||
|   res.setHeader("Set-Cookie", adminCookie); | ||||
| } | ||||
|  | ||||
| function deleteAdminCookie(res: Response) { | ||||
|   const adminCookie = cookie.serialize("admin", false, { | ||||
|     path: "/", | ||||
|     maxAge: 1, | ||||
|   }); | ||||
|   res.setHeader("Set-Cookie", adminCookie); | ||||
| } | ||||
|  | ||||
| function login(req: Request, res: Response) { | ||||
|   const { username, password } = req.body; | ||||
|  | ||||
|   if (username !== "admin" || password !== "admin") { | ||||
|     return res.status(403).send({ | ||||
|       success: false, | ||||
|       message: "Feil brukernavn eller passord", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   addAdminCookie(res); | ||||
|   res.send({ | ||||
|     success: true, | ||||
|     message: "Velkommen!", | ||||
|   }); | ||||
| } | ||||
|  | ||||
| function logout(req: Request, res: Response) { | ||||
|   deleteAdminCookie(res); | ||||
|   res.status(200).send("ok"); | ||||
| } | ||||
|  | ||||
| export default { login, logout }; | ||||
							
								
								
									
										381
									
								
								src/webserver/controllers/orderController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								src/webserver/controllers/orderController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,381 @@ | ||||
| import logger from "../../logger"; | ||||
| import OrderRepository from "../../order"; | ||||
| import CustomerRepository from "../../customer"; | ||||
| import ProductRepository from "../../product"; | ||||
| import WarehouseRepository from "../../warehouse"; | ||||
| import Cart from "../../cart/Cart"; | ||||
|  | ||||
| import ICustomer from "../../interfaces/ICustomer"; | ||||
|  | ||||
| import { validEmail } from "../../utils/formValidation"; | ||||
| import ICart from "../../interfaces/ICart"; | ||||
|  | ||||
| const orderRepository = new OrderRepository(); | ||||
| const customerRepository = new CustomerRepository(); | ||||
| const productRepository = new ProductRepository(); | ||||
| const warehouseRepository = new WarehouseRepository(); | ||||
|  | ||||
| async function validateCart(cart: ICart[]) { | ||||
|   const validationErrors = []; | ||||
|  | ||||
|   for (let i = 0; i < cart.length; i++) { | ||||
|     const { sku_id, quantity, name, lineitem_id } = cart[i]; | ||||
|     const product = await productRepository.get(sku_id); | ||||
|     if (!product) { | ||||
|       validationErrors.push({ | ||||
|         type: "order-summary", | ||||
|         field: `lineitem-${lineitem_id}`, | ||||
|         message: `Fant ikke produktet ${name}.`, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     // check if in stock | ||||
|     const leftInStockResponse = await warehouseRepository.checkSkuStock(sku_id); | ||||
|     if (!leftInStockResponse || quantity > leftInStockResponse?.stock) { | ||||
|       validationErrors.push({ | ||||
|         type: "order-summary", | ||||
|         field: `lineitem-${lineitem_id}`, | ||||
|         message: `Det er bare ${leftInStockResponse?.stock} igjen av denne varen på lager.`, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return validationErrors; | ||||
| } | ||||
|  | ||||
| function validateCustomer(customer: ICustomer) { | ||||
|   const { | ||||
|     email, | ||||
|     first_name, | ||||
|     last_name, | ||||
|     street_address, | ||||
|     zip_code, | ||||
|     city, | ||||
|   } = customer; | ||||
|   const validationErrors = []; | ||||
|  | ||||
|   if (!email?.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "email", | ||||
|       message: "Epost adresse er påkrevd", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (!validEmail(email)) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "email", | ||||
|       message: "Epost addressen er ikke gyldig", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (!first_name.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "first_name", | ||||
|       message: "Fornavn er påkrevd", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (!last_name.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "last_name", | ||||
|       message: "Etternavn er påkrevd", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (!street_address.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "street_address", | ||||
|       message: "Gateadresse er påkrevd", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   const _zipcode = String(zip_code || ""); | ||||
|   if (!_zipcode.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "zip_code", | ||||
|       message: "Postnummer er påkrevd", | ||||
|     }); | ||||
|   } else if (_zipcode.length !== 3 && _zipcode.length !== 4) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "zip_code", | ||||
|       message: "Postnummer må være 4 siffer", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (!city.length) { | ||||
|     validationErrors.push({ | ||||
|       type: "customer", | ||||
|       field: "city", | ||||
|       message: "By er påkrevd", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   return validationErrors; | ||||
| } | ||||
|  | ||||
| async function getAll(req, res) { | ||||
|   logger.info("Getting all orders"); | ||||
|   try { | ||||
|     const orders = await orderRepository.getAll(); | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       orders, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while getting all orders", { error }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: "Unexpected error while getting all orders", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| // interface IOrderFormError { | ||||
| //   type: string (enum) | ||||
| //   field: string (enum) // the field is linked to html form name | ||||
| //   message: string | ||||
| // } | ||||
|  | ||||
| async function createOrder(req, res) { | ||||
|   const cart: ICart[] = req.body?.cart; | ||||
|   const customer: ICustomer = req.body?.customer; | ||||
|  | ||||
|   logger.info("Submitting new order", { customer, cart }); | ||||
|  | ||||
|   // check if product exists | ||||
|   let validationErrors = []; | ||||
|   try { | ||||
|     validationErrors = validationErrors.concat(validateCustomer(customer)); | ||||
|     validationErrors = validationErrors.concat(await validateCart(cart)); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while validation order", { error }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     return res.send({ | ||||
|       success: false, | ||||
|       form_input: null, | ||||
|       message: error?.message || "Unable to validate order", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (validationErrors.length) { | ||||
|     logger.error("Validation error when submitting order", { | ||||
|       validationErrors, | ||||
|     }); | ||||
|     res.statusCode = 400; | ||||
|  | ||||
|     return res.send({ | ||||
|       success: false, | ||||
|       validationErrors, | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   try { | ||||
|     const { customer_no } = await customerRepository.newCustomer(customer); | ||||
|     const { order_id } = await orderRepository.newOrder(customer_no); | ||||
|     await Promise.all( | ||||
|       cart.map((lineItem) => | ||||
|         orderRepository.addOrderLineItem( | ||||
|           order_id, | ||||
|           lineItem.product_no, | ||||
|           lineItem.sku_id, | ||||
|           lineItem.price, | ||||
|           lineItem.quantity | ||||
|         ) | ||||
|       ) | ||||
|     ); | ||||
|  | ||||
|     logger.info("Sucessfully created order", { order_id, customer_no }); | ||||
|     return res.send({ | ||||
|       success: true, | ||||
|       message: "Sucessfull created order!", | ||||
|       order_id, | ||||
|       customer_no, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while creating customer or order", { error }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     return res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while creating order", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function get(req, res) { | ||||
|   const { order_id } = req.params; | ||||
|   logger.info("Getting order by id", { order_id }); | ||||
|  | ||||
|   // get a order | ||||
|   let order = null; | ||||
|   try { | ||||
|     order = await orderRepository.getOrderDetailed(order_id); | ||||
|     logger.info("Found order", { order }); | ||||
|  | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       order, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while looking for order", { order_id, error }); | ||||
|     res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while getting order", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function getOrderStatus(req, res) { | ||||
|   const { order_id } = req.params; | ||||
|   logger.info("Getting order status by id", { order_id }); | ||||
|  | ||||
|   return await orderRepository.getOrderStatus(order_id).then((orderStatus) => { | ||||
|     logger.info("Found order status", { order_id, orderStatus }); | ||||
|  | ||||
|     if (orderStatus) { | ||||
|       res.send({ | ||||
|         status: orderStatus?.status, | ||||
|         order_id, | ||||
|         success: true, | ||||
|       }); | ||||
|     } else { | ||||
|       logger.error("Error while getting order status", { order_id }); | ||||
|  | ||||
|       res.status(500).send({ | ||||
|         initiated: null, | ||||
|         confirmed: null, | ||||
|         message: "Unexpeted error! Unable to get order status", | ||||
|         success: false, | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| } | ||||
|  | ||||
| // async function cancelOrder(req, res) { | ||||
| //   const { id } = req.params; | ||||
| //   let orderId = id; | ||||
| //   const vippsId = id; | ||||
| //   await vippsRepository.getOrder(id).then((order) => { | ||||
| //     if (order && order.parent_order_id) { | ||||
| //       orderId = order.parent_order_id; | ||||
| //     } | ||||
| //   }); | ||||
| //   return vippsRepository | ||||
| //     .cancelOrRefundPartialOrder(vippsId, req.id) | ||||
| //     .then((order) => PlsController.turnOff(order)) | ||||
| //     .then((_) => | ||||
| //       orderRepository.cancelOrder(orderId).then((canceled) => | ||||
| //         res.send({ | ||||
| //           success: true, | ||||
| //           canceled: canceled, | ||||
| //         }) | ||||
| //       ) | ||||
| //     ) | ||||
| //     .catch((error) => { | ||||
| //       throw error; | ||||
| //     }); | ||||
| // } | ||||
|  | ||||
| // function verifyNoCollidingOrders(id) { | ||||
| //   return orderRepository | ||||
| //     .getOrder(id) | ||||
| //     .then((order) => orderRepository.getConflictingProductOrders(order)) | ||||
| //     .then(({ order, conflicting }) => checkForConflicting(order, conflicting)); | ||||
| // } | ||||
|  | ||||
| // function checkForConflicting(order, conflicting) { | ||||
| //   const thisOrderCreated = new Date(order.created); | ||||
| //   for (var i = 0; i < conflicting.length; i++) { | ||||
| //     const thisConflictinCreated = new Date(conflicting[i].created); | ||||
| //     // if we are anywhere with a conflict, we need to cancel/refund this | ||||
| //     if (thisOrderCreated > thisConflictinCreated) { | ||||
| //       throw new WagoError( | ||||
| //         WAGO_ERROR_STATUS_CODES.CONFLICTING_ORDER_RESERVATION | ||||
| //       ); | ||||
| //     } | ||||
| //   } | ||||
| //   return order; | ||||
| // } | ||||
|  | ||||
| // async function extendOrder(req, res) { | ||||
| //   let { amount } = req.body; | ||||
| //   let extendeeOrderId = req.params.id; | ||||
| //   let extendedTimes = 1; | ||||
| //   const previousExtendedAndPreviousOrder = await vippsRepository | ||||
| //     .getOrder(extendeeOrderId) | ||||
| //     .then((order) => { | ||||
| //       if (order.parent_order_id) { | ||||
| //         extendeeOrderId = order.parent_order_id; | ||||
| //       } | ||||
| //       return orderRepository.getExtendedOrders(extendeeOrderId); | ||||
| //     }); | ||||
|  | ||||
| //   let orderId = `${extendeeOrderId}-ext-`; | ||||
| //   if (!previousExtendedAndPreviousOrder || amount < 0) { | ||||
| //     res.status(404).send({ success: false }); | ||||
| //     return; | ||||
| //   } | ||||
|  | ||||
| //   for (let i = 0; i < previousExtendedAndPreviousOrder.length; i++) { | ||||
| //     const currentElement = previousExtendedAndPreviousOrder[i]; | ||||
| //     if (currentElement.order_id !== extendeeOrderId) { | ||||
| //       extendedTimes += 1; | ||||
| //     } | ||||
| //   } | ||||
|  | ||||
| //   orderId += extendedTimes; | ||||
| //   const orderWithProductData = await orderRepository.getOrderWithProduct( | ||||
| //     extendeeOrderId | ||||
| //   ); | ||||
|  | ||||
| //   try { | ||||
| //     amount = parseFloat(amount); | ||||
| //   } catch (e) {} | ||||
| //   const moneyToPay = orderWithProductData.price * 100 * amount; | ||||
|  | ||||
| //   return vippsRepository | ||||
| //     .newExtendedPayment( | ||||
| //       orderId, | ||||
| //       extendeeOrderId, | ||||
| //       moneyToPay, | ||||
| //       amount, | ||||
| //       orderWithProductData | ||||
| //     ) | ||||
| //     .then((resp) => { | ||||
| //       res.send({ | ||||
| //         success: true, | ||||
| //         ...resp, | ||||
| //       }); | ||||
| //     }) | ||||
| //     .catch((error) => { | ||||
| //       res.statusCode = error.statusCode || 500; | ||||
|  | ||||
| //       res.send({ | ||||
| //         success: false, | ||||
| //         ...error, | ||||
| //       }); | ||||
| //     }); | ||||
| // } | ||||
|  | ||||
| export default { | ||||
|   createOrder, | ||||
|   get, | ||||
|   getAll, | ||||
|   getOrderStatus, | ||||
|   // cancelOrder, | ||||
|   extendOrder: () => {}, | ||||
|   checkForConflicting: () => {}, | ||||
|   verifyNoCollidingOrders: () => {}, | ||||
| }; | ||||
							
								
								
									
										237
									
								
								src/webserver/controllers/productController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										237
									
								
								src/webserver/controllers/productController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,237 @@ | ||||
| import logger from "../../logger"; | ||||
| import ProductRepository from "../../product"; | ||||
| const productRepository = new ProductRepository(); | ||||
| import type { Request, Response } from "express"; | ||||
|  | ||||
| async function add(req: Request, res: Response) { | ||||
|   logger.info("Adding new product"); | ||||
|   try { | ||||
|     const productId = await productRepository.add(); | ||||
|     const product = await productRepository.get(productId); | ||||
|     logger.info("New product", { product }); | ||||
|  | ||||
|     return res.send({ | ||||
|       success: true, | ||||
|       product, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while adding product", { error }); | ||||
|     res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|     return res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while adding product", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| function update(req: Request, res: Response) { | ||||
|   const { product_id } = req.params; | ||||
|   logger.info("Updating product", { product_id }); | ||||
|  | ||||
|   return productRepository | ||||
|     .get(product_id) | ||||
|     .then((product) => { | ||||
|       logger.info("Updated product", { product, product_id }); | ||||
|  | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         product: product, | ||||
|       }); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       logger.error("Error while updating product", { error, product_id }); | ||||
|       res.statusCode = error.statusCode || 500; | ||||
|       return res.send({ | ||||
|         success: false, | ||||
|         message: error?.message || "Unexpected error while updating product", | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function getAll(req: Request, res: Response) { | ||||
|   logger.info("Getting all products"); | ||||
|  | ||||
|   return productRepository | ||||
|     .getAllProducts() | ||||
|     .then((products) => { | ||||
|       logger.info("Found products", { products }); | ||||
|  | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         products: products, | ||||
|       }); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       logger.error("Error while getting all products", { error }); | ||||
|       res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|       res.send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || "Unexpected error while getting all products", | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function getById(req: Request, res: Response) { | ||||
|   const { product_id } = req.params; | ||||
|   logger.info("Getting product", { product_id }); | ||||
|  | ||||
|   return productRepository | ||||
|     .get(product_id) | ||||
|     .then((product) => { | ||||
|       logger.info("Found product", { product, product_id }); | ||||
|  | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         product: product, | ||||
|       }); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       logger.error("Error while getting product by id", { product_id }); | ||||
|       res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|       res.send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || "Unexpected error while getting product by id", | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| async function addSku(req: Request, res: Response) { | ||||
|   const { product_id } = req.params; | ||||
|   logger.info("Adding new sku", { product_id }); | ||||
|  | ||||
|   try { | ||||
|     await productRepository.addSku(product_id); | ||||
|     const skus = await productRepository.getSkus(product_id); | ||||
|  | ||||
|     if (!skus.find((sku) => sku.default_price === true)) { | ||||
|       const setDefaultResponse = await productRepository.setSkuDefaultPrice( | ||||
|         product_id, | ||||
|         skus[skus.length - 1].sku_id | ||||
|       ); | ||||
|  | ||||
|       skus[skus.length - 1].default_price = true; | ||||
|     } | ||||
|     logger.info("New skus after add", { skus, product_id }); | ||||
|  | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       skus, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error adding sku", { error, product_id }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while adding new sku", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function getSkus(req: Request, res: Response) { | ||||
|   const { product_id } = req.params; | ||||
|   const skus = await productRepository.getSkus(product_id); | ||||
|  | ||||
|   return res.send({ | ||||
|     success: true, | ||||
|     skus, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| async function updateSku(req: Request, res: Response) { | ||||
|   const { product_id, sku_id } = req.params; | ||||
|   const { stock, size, price } = req.body; | ||||
|   logger.info("Updating sku", { product_id, sku_id, stock, price, size }); | ||||
|  | ||||
|   try { | ||||
|     await productRepository.updateSku(product_id, sku_id, stock, size, price); | ||||
|     const skus = await productRepository.getSkus(product_id); | ||||
|     logger.info("New skus after update", { skus, product_id, sku_id }); | ||||
|  | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       skus, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error updating sku", { product_id, sku_id, error }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while updating sku", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function deleteSku(req: Request, res: Response) { | ||||
|   const { product_id, sku_id } = req.params; | ||||
|  | ||||
|   try { | ||||
|     await productRepository.deleteSku(product_id, sku_id); | ||||
|     const skus = await productRepository.getSkus(product_id); | ||||
|     logger.info("New skus after delete", { skus, product_id, sku_id }); | ||||
|  | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       skus, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error deleting sku", { product_id, sku_id, error }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: error?.message || "Unexpected error while deleting sku", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| async function setSkuDefaultPrice(req: Request, res: Response) { | ||||
|   const { product_id, sku_id } = req.params; | ||||
|   logger.info("Updating sku default price", { product_id, sku_id }); | ||||
|  | ||||
|   try { | ||||
|     await productRepository.setSkuDefaultPrice(product_id, sku_id); | ||||
|     const skus = await productRepository.getSkus(product_id); | ||||
|     logger.info("New skus after update default price", { | ||||
|       skus, | ||||
|       product_id, | ||||
|       sku_id, | ||||
|     }); | ||||
|  | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       skus, | ||||
|     }); | ||||
|   } catch (error) { | ||||
|     logger.error("Error while updating sku default price", { | ||||
|       product_id, | ||||
|       sku_id, | ||||
|       error, | ||||
|     }); | ||||
|     res.statusCode = error?.statusCode || 500; | ||||
|  | ||||
|     res.send({ | ||||
|       success: false, | ||||
|       message: | ||||
|         error?.message || | ||||
|         "Unexpected error while updating default price for sku", | ||||
|     }); | ||||
|   } | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   add, | ||||
|   update, | ||||
|   getAll, | ||||
|   getById, | ||||
|   addSku, | ||||
|   getSkus, | ||||
|   updateSku, | ||||
|   deleteSku, | ||||
|   setSkuDefaultPrice, | ||||
| }; | ||||
							
								
								
									
										94
									
								
								src/webserver/controllers/stripePaymentController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								src/webserver/controllers/stripePaymentController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| import Configuration from "../../config/configuration"; | ||||
| import StripeApi from "../../stripe/stripeApi"; | ||||
| import StripeRepository from "../../stripe/stripeRepository"; | ||||
| import OrderRepository from "../../order"; | ||||
| import CustomerRepository from "../../customer"; | ||||
|  | ||||
| import Stripe from "stripe"; | ||||
|  | ||||
| import type { Request, Response, NextFunction } from "express"; | ||||
| import type { IOrder, ILineItem } from "../../interfaces/IOrder"; | ||||
| import type ICustomer from "../../interfaces/ICustomer"; | ||||
|  | ||||
| const configuration = Configuration.getInstance(); | ||||
| const stripePublicKey = configuration.get("stripe", "publicKey"); | ||||
| const stripeSecretKey = configuration.get("stripe", "secretKey"); | ||||
| const stripeApi = new StripeApi(stripePublicKey, stripeSecretKey); | ||||
| const stripeRepository = new StripeRepository(); | ||||
| const orderRepository = new OrderRepository(); | ||||
| const customerRepository = new CustomerRepository(); | ||||
|  | ||||
| async function create(req, res) { | ||||
|   const clientId = req?.planetId; | ||||
|   const { order_id, customer_no } = req.body; | ||||
|  | ||||
|   if (!order_id || !customer_no) { | ||||
|     return res.status(400).send({ | ||||
|       success: false, | ||||
|       message: "Missing order_id and/or customer_id", | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   const order: IOrder = await orderRepository.getOrder(order_id); | ||||
|   const customer: ICustomer = await customerRepository.getCustomer(customer_no); | ||||
|  | ||||
|   const sum = order.lineItems?.reduce( | ||||
|     (total, lineItem: ILineItem) => total + lineItem.quantity * lineItem.price, | ||||
|     0 | ||||
|   ); | ||||
|  | ||||
|   stripeRepository | ||||
|     .createPayment(clientId, sum, order_id, customer) | ||||
|     .then((clientSecret) => | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         clientSecret, | ||||
|       }) | ||||
|     ); | ||||
| } | ||||
|  | ||||
| async function updatePayment(req: Request, res: Response) { | ||||
|   console.log("STRIPE WEBHOOK body:", req.body); | ||||
|   const { type, data } = req.body; | ||||
|   const { object } = data; | ||||
|   const { orderId } = object?.metadata; | ||||
|  | ||||
|   if (!data) { | ||||
|     console.log("no data found in webhook, nothing to do"); | ||||
|     return res.status(200).send("ok"); | ||||
|   } | ||||
|  | ||||
|   if (!orderId) { | ||||
|     console.log("no order_id found in webhook, nothing to do"); | ||||
|     return res.status(200).send("ok"); | ||||
|   } | ||||
|  | ||||
|   if (type === "payment_intent.created") { | ||||
|     console.log("handle payment intent created... doing nothing"); | ||||
|   } else if (type === "payment_intent.succeeded") { | ||||
|     console.log("handle payment succeeded", object); | ||||
|     await stripeRepository.updatePaymentIntent(object); | ||||
|     orderRepository.confirmOrder(orderId); | ||||
|   } else if (type === "payment_intent.payment_failed") { | ||||
|     console.log("handle payment failed", object); | ||||
|     await stripeRepository.updatePaymentIntent(object); | ||||
|     orderRepository.cancelOrder(orderId); | ||||
|   } else if (type === "charge.succeeded") { | ||||
|     console.log("handle charge succeeded", object); | ||||
|     await stripeRepository.updatePaymentCharge(object); | ||||
|   } else if (type === "charge.refunded") { | ||||
|     console.log("handle charge refunded", object); | ||||
|     await stripeRepository.updatePaymentCharge(object); | ||||
|     await orderRepository.refundOrder(orderId); | ||||
|   } else { | ||||
|     console.log(`webhook for ${type}, not setup yet`); | ||||
|   } | ||||
|  | ||||
|   // should always return 200 but should try catch and log error | ||||
|   return res.status(200).send("ok"); | ||||
| } | ||||
|  | ||||
| export default { | ||||
|   create, | ||||
|   updatePayment, | ||||
| }; | ||||
							
								
								
									
										190
									
								
								src/webserver/controllers/vippsPaymentController.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								src/webserver/controllers/vippsPaymentController.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | ||||
| const { VippsApiResourceNotFound } = require(`${__base}/vipps/vippsApiErrors`); | ||||
| const { WagoError, WAGO_ERROR_STATUS_CODES } = require(`${__base}/errors`); | ||||
| const { VIPPS_STATUS_CODES } = require(`${__enums}/vipps`); | ||||
| const OrderRepository = require(`${__base}/order`); | ||||
| const OrderController = require(`${__controllers}/orderController`); | ||||
|  | ||||
| const orderRepository = new OrderRepository(); | ||||
| const Vipps = require(`${__base}/vipps`); | ||||
| const VippsRepository = require(`${__base}/vipps/vippsRepository`); | ||||
|  | ||||
| const vipps = new Vipps(); | ||||
| const vippsRepository = new VippsRepository(); | ||||
|  | ||||
| function getPaymentDetails(req, res) { | ||||
|   console.log("hit get details payment"); | ||||
|  | ||||
|   const { id } = req.params; | ||||
|   return vipps | ||||
|     .getPayment(id) | ||||
|     .then((payment) => | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         payment, | ||||
|       }) | ||||
|     ) | ||||
|     .catch((err) => { | ||||
|       const { statusCode, message } = err; | ||||
|  | ||||
|       return res.status(statusCode || 500).send({ | ||||
|         success: false, | ||||
|         payment: null, | ||||
|         message: message, | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function cancelPayment(req, res) { | ||||
|   console.log("hit cancelOrder endpoint"); | ||||
|   const { id } = req.params; | ||||
|  | ||||
|   return vippsRepository.cancelPayment(id).then((canceled) => | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       canceled: canceled, | ||||
|     }) | ||||
|   ); | ||||
| } | ||||
|  | ||||
| function rejectPayment(id, amount) { | ||||
|   console.log("rejectPayment"); | ||||
|   return vippsRepository | ||||
|     .cancelPayment(id) | ||||
|     .then((_) => | ||||
|       vippsRepository.updatePaymentStatus( | ||||
|         id, | ||||
|         VIPPS_STATUS_CODES.REJECTED, | ||||
|         amount | ||||
|       ) | ||||
|     ) | ||||
|     .then((_) => orderRepository.rejectByVippsTimeout(id)); | ||||
| } | ||||
|  | ||||
| function rejectByVippsTimeout(id, amount) { | ||||
|   console.log("reject-by-timeout"); | ||||
|   return vippsRepository | ||||
|     .cancelPayment(id) | ||||
|     .then((_) => | ||||
|       vippsRepository.updatePaymentStatus( | ||||
|         id, | ||||
|         VIPPS_STATUS_CODES.TIMED_OUT_REJECT, | ||||
|         amount | ||||
|       ) | ||||
|     ) | ||||
|     .then((_) => orderRepository.rejectByVippsTimeout(id)); | ||||
| } | ||||
|  | ||||
| function updatePayment(req, res) { | ||||
|   console.log("updatePayment:", req.body); | ||||
|   const { id } = req.params; | ||||
|   const { | ||||
|     transactionInfo: { status }, | ||||
|     errorInfo, | ||||
|   } = req.body; | ||||
|  | ||||
|   let statusCode; | ||||
|   if (status && VIPPS_STATUS_CODES[status]) { | ||||
|     statusCode = VIPPS_STATUS_CODES[status]; | ||||
|   } | ||||
|  | ||||
|   // TODO: Don't cancel whole order when one new order | ||||
|   // fails/gets rejected/cancels | ||||
|   if (status === VIPPS_STATUS_CODES.REJECTED) { | ||||
|     switch (errorInfo.errorCode) { | ||||
|       case 45: | ||||
|       default: | ||||
|         return vippsRepository | ||||
|           .cancelOrRefundPartialOrder(id) | ||||
|           .then((_) => orderRepository.cancelOrder(id)); | ||||
|     } | ||||
|   } else if (status === VIPPS_STATUS_CODES.CANCELLED) { | ||||
|     return vippsRepository | ||||
|       .cancelOrRefundPartialOrder(id) | ||||
|       .then((_) => orderRepository.cancelOrder(id)); | ||||
|   } | ||||
|  | ||||
|   return vippsRepository | ||||
|     .getOrder(id) | ||||
|     .then((order) => extendOrUpdateNewOrder(order, statusCode, req)) | ||||
|     .then((_) => res.status(200).send()) | ||||
|     .catch((error) => { | ||||
|       console.log("we errored here", error); | ||||
|       return vippsRepository | ||||
|         .cancelOrRefundPartialOrder(id) | ||||
|         .then((_) => orderRepository.cancelOrder(id)); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function extendOrUpdateNewOrder(order, statusCode, req) { | ||||
|   const { | ||||
|     transactionInfo: { transactionId, amount }, | ||||
|   } = req.body; | ||||
|   const vippsId = order.order_id; | ||||
|   let orderId = order.order_id; | ||||
|   if (order.parent_order_id) { | ||||
|     orderId = order.parent_order_id; | ||||
|   } | ||||
|   return vippsRepository | ||||
|     .updatePaymentStatus(vippsId, statusCode, amount, transactionId) | ||||
|     .then((_) => { | ||||
|       if (order.parent_order_id) { | ||||
|         return getNewTimeToTurnOn(order); | ||||
|         // return getNewTimeToTurnOn(order).then(hours => | ||||
|         //   PlsController.extendTime(hours, order) | ||||
|         // ); | ||||
|       } | ||||
|  | ||||
|       return OrderController.verifyNoCollidingOrders(orderId); | ||||
|       // return OrderController.verifyNoCollidingOrders(orderId).then( | ||||
|       //   async order => await PlsController.sendDuration(order) | ||||
|       // ); | ||||
|     }) | ||||
|     .then(async (plsResponse) => { | ||||
|       if (plsResponse.startTime) { | ||||
|         await orderRepository.updateStartTime(orderId, plsResponse.startTime); | ||||
|       } | ||||
|       return orderRepository | ||||
|         .updateEndTime(orderId, plsResponse.endTime) | ||||
|         .then((_) => | ||||
|           vippsRepository.updateEndTime(vippsId, plsResponse.endTime) | ||||
|         ); | ||||
|     }) | ||||
|     .then((_) => vipps.captureAmount(vippsId, amount, req.id)) | ||||
|     .then((capture) => | ||||
|       vippsRepository.updatePaymentCapture( | ||||
|         vippsId, | ||||
|         capture.transactionSummary.capturedAmount | ||||
|       ) | ||||
|     ); | ||||
| } | ||||
|  | ||||
| async function getNewTimeToTurnOn(order) { | ||||
|   const previousExtendedAndPreviousOrder = await orderRepository.getExtendedOrders( | ||||
|     order.parent_order_id | ||||
|   ); | ||||
|   let hoursToExpandAndSendToPls = 0; | ||||
|   let currentEndTime; | ||||
|   const now = new Date(); | ||||
|  | ||||
|   for (let i = 0; i < previousExtendedAndPreviousOrder.length; i++) { | ||||
|     const currentElement = previousExtendedAndPreviousOrder[i]; | ||||
|  | ||||
|     if (currentElement.order_id === order.parent_order_id) { | ||||
|       currentEndTime = new Date(currentElement.end_time); | ||||
|       if (currentEndTime < now) { | ||||
|         currentEndTime = now; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   const timeLeft = (currentEndTime.getTime() - now.getTime()) / 36e5; | ||||
|   hoursToExpandAndSendToPls = order.hours + timeLeft; | ||||
|   return hoursToExpandAndSendToPls; | ||||
| } | ||||
|  | ||||
| module.exports = { | ||||
|   getPaymentDetails, | ||||
|   cancelPayment, | ||||
|   extendOrUpdateNewOrder, | ||||
|   updatePayment, | ||||
|   getNewTimeToTurnOn, | ||||
| }; | ||||
							
								
								
									
										12
									
								
								src/webserver/controllers/vippsTokenController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/webserver/controllers/vippsTokenController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,12 @@ | ||||
| const Vipps = require(`${__base}/vipps`); | ||||
| const vipps = new Vipps(); | ||||
| import type { Request, Response } from "express"; | ||||
|  | ||||
| export default function VippsTokenController(req: Request, res: Response) { | ||||
|   return vipps.token.then((token) => | ||||
|     res.send({ | ||||
|       success: true, | ||||
|       token: token, | ||||
|     }) | ||||
|   ); | ||||
| } | ||||
							
								
								
									
										65
									
								
								src/webserver/controllers/warehouseController.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/webserver/controllers/warehouseController.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| import logger from "../../logger"; | ||||
| import WarehouseRepository from "../../warehouse"; | ||||
| const warehouseRepository = new WarehouseRepository(); | ||||
| import type { Request, Response } from "express"; | ||||
|  | ||||
| function getAll(req: Request, res: Response) { | ||||
|   logger.info("Fething all warehouse products"); | ||||
|  | ||||
|   return warehouseRepository | ||||
|     .all() | ||||
|     .then((warehouseProucts) => { | ||||
|       logger.info("Found warehouse products", { products: warehouseProucts }); | ||||
|  | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         warehouse: warehouseProucts, | ||||
|       }); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       logger.error("Error fetching warehouse products", { error }); | ||||
|       res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|       res.send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           "Unexpected error while fetching all warehouse products", | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function getProduct(req: Request, res: Response) { | ||||
|   const { productId } = req.params; | ||||
|   logger.info("Fetching warehouse product", { product_id: productId }); | ||||
|  | ||||
|   return warehouseRepository | ||||
|     .getProduct(productId) | ||||
|     .then((product) => { | ||||
|       logger.info("Found warehouse product", { | ||||
|         product, | ||||
|         product_id: productId, | ||||
|       }); | ||||
|  | ||||
|       res.send({ | ||||
|         success: true, | ||||
|         product: product, | ||||
|       }); | ||||
|     }) | ||||
|     .catch((error) => { | ||||
|       logger.error("Error fetching warehouse product:", { | ||||
|         error, | ||||
|         product_id: productId, | ||||
|       }); | ||||
|       res.statusCode = error.statusCode || 500; | ||||
|  | ||||
|       res.send({ | ||||
|         success: false, | ||||
|         message: | ||||
|           error?.message || | ||||
|           `Unexpected error while fetching product with id: ${productId}`, | ||||
|       }); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| export default { getAll, getProduct }; | ||||
							
								
								
									
										37
									
								
								src/webserver/middleware/getOrSetCookieForClient.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/webserver/middleware/getOrSetCookieForClient.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| import cookie from "cookie"; | ||||
| import generateUUID from "../../utils/generateUUID"; | ||||
| import httpContext from "express-http-context"; | ||||
| import type { Request, Response, NextFunction } from "express"; | ||||
|  | ||||
| const cookieClientKey = "planetId"; | ||||
| const cookieOptions = { | ||||
|   path: "/", | ||||
|   maxAge: 60 * 60 * 24 * 7, // 7 days | ||||
| }; | ||||
|  | ||||
| function setClientIdCookieHeader(res: Response, value: string) { | ||||
|   const setCookie = cookie.serialize("planetId", value, cookieOptions); | ||||
|   return res.setHeader("Set-Cookie", setCookie); | ||||
| } | ||||
|  | ||||
| const getOrSetCookieForClient = ( | ||||
|   req: Request, | ||||
|   res: Response, | ||||
|   next: NextFunction | ||||
| ) => { | ||||
|   const cookies = cookie.parse(req.headers.cookie || ""); | ||||
|   const planetId = cookies[cookieClientKey]; | ||||
|  | ||||
|   if (planetId) { | ||||
|     req.planetId = planetId; | ||||
|     httpContext.set("planetId", planetId); | ||||
|     return next(); | ||||
|   } | ||||
|  | ||||
|   const clientId = generateUUID(); | ||||
|   setClientIdCookieHeader(res, clientId); | ||||
|  | ||||
|   next(); | ||||
| }; | ||||
|  | ||||
| export default getOrSetCookieForClient; | ||||
							
								
								
									
										8
									
								
								src/webserver/middleware/setupCORS.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/webserver/middleware/setupCORS.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| import type { Request, Response, NextFunction } from "express"; | ||||
|  | ||||
| const openCORS = (req: Request, res: Response, next: NextFunction) => { | ||||
|   res.set("Access-Control-Allow-Origin", "*"); | ||||
|   return next(); | ||||
| }; | ||||
|  | ||||
| export default openCORS; | ||||
							
								
								
									
										43
									
								
								src/webserver/middleware/setupHeaders.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/webserver/middleware/setupHeaders.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,43 @@ | ||||
| import type { Request, Response, NextFunction } from "express"; | ||||
|  | ||||
| const camelToKebabCase = (str: string) => | ||||
|   str.replace(/[A-Z]/g, (letter: string) => `-${letter.toLowerCase()}`); | ||||
|  | ||||
| const mapFeaturePolicyToString = (features) => { | ||||
|   return Object.entries(features) | ||||
|     .map(([key, value]) => { | ||||
|       key = camelToKebabCase(key); | ||||
|       value = value == "*" ? value : `'${value}'`; | ||||
|       return `${key} ${value}`; | ||||
|     }) | ||||
|     .join("; "); | ||||
| }; | ||||
|  | ||||
| const setupHeaders = (req: Request, res: Response, next: NextFunction) => { | ||||
|   res.set("Access-Control-Allow-Headers", "Content-Type"); | ||||
|   res.set("Access-Control-Allow-Methods", "POST, PATCH, DELETE"); | ||||
|  | ||||
|   // Security | ||||
|   res.set("X-Content-Type-Options", "nosniff"); | ||||
|   res.set("X-XSS-Protection", "1; mode=block"); | ||||
|   res.set("X-Frame-Options", "SAMEORIGIN"); | ||||
|   res.set("X-DNS-Prefetch-Control", "off"); | ||||
|   res.set("X-Download-Options", "noopen"); | ||||
|   res.set("Strict-Transport-Security", "max-age=15552000; includeSubDomains"); | ||||
|  | ||||
|   // Feature policy | ||||
|   const features = { | ||||
|     fullscreen: "*", | ||||
|     payment: "none", | ||||
|     microphone: "none", | ||||
|     camera: "self", | ||||
|     speaker: "*", | ||||
|     syncXhr: "self", | ||||
|   }; | ||||
|   const featureString = mapFeaturePolicyToString(features); | ||||
|   res.set("Feature-Policy", featureString); | ||||
|  | ||||
|   return next(); | ||||
| }; | ||||
|  | ||||
| export default setupHeaders; | ||||
							
								
								
									
										79
									
								
								src/webserver/server.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/webserver/server.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | ||||
| // import * as global from "../types/global"; | ||||
| import path from "path"; | ||||
| import express from "express"; | ||||
| import { createServer } from "http"; | ||||
|  | ||||
| // services | ||||
| import logger from "../logger"; | ||||
| import { setupCartWebsocketServer } from "./websocketCartServer"; | ||||
|  | ||||
| // controllers | ||||
| // const TokenController = require(`${__controllers}/tokenController`); | ||||
| import OrderController from "./controllers/orderController"; | ||||
| import ProductController from "./controllers/productController"; | ||||
| import WarehouseController from "./controllers/warehouseController"; | ||||
| import StripePaymentController from "./controllers/stripePaymentController"; | ||||
| import LoginController from "./controllers/loginController"; | ||||
|  | ||||
| // middleware | ||||
| import httpContext from "express-http-context"; | ||||
| import setupCORS from "./middleware/setupCORS"; | ||||
| import setupHeaders from "./middleware/setupHeaders"; | ||||
| import getOrSetCookieForClient from "./middleware/getOrSetCookieForClient"; | ||||
|  | ||||
| const app = express(); | ||||
| const port = process.env.PORT || 30010; | ||||
|  | ||||
| app.use(httpContext.middleware); | ||||
| app.use(setupCORS); | ||||
| app.use(setupHeaders); | ||||
| app.use(getOrSetCookieForClient); | ||||
|  | ||||
| // parse application/json | ||||
|  | ||||
| const router = express.Router(); | ||||
| router.use(express.json()); | ||||
|  | ||||
| router.post("/login", LoginController.login); | ||||
| router.post("/logout", LoginController.logout); | ||||
|  | ||||
| router.get("/products", ProductController.getAll); | ||||
| router.post("/product", ProductController.add); | ||||
| router.get("/product/:product_id", ProductController.getById); | ||||
| router.post("/product/:product_id/sku", ProductController.addSku); | ||||
| router.patch("/product/:product_id/sku/:sku_id", ProductController.updateSku); | ||||
| router.delete("/product/:product_id/sku/:sku_id", ProductController.deleteSku); | ||||
| router.post( | ||||
|   "/product/:product_id/sku/:sku_id/default_price", | ||||
|   ProductController.setSkuDefaultPrice | ||||
| ); | ||||
|  | ||||
| router.get("/orders", OrderController.getAll); | ||||
| router.post("/order", OrderController.createOrder); | ||||
| router.get("/order/:order_id", OrderController.get); | ||||
| router.get("/order/:order_id/status", OrderController.getOrderStatus); | ||||
|  | ||||
| router.get("/warehouse", WarehouseController.getAll); | ||||
| router.get("/warehouse/:productId", WarehouseController.getProduct); | ||||
| // router.get("/api/order/:id", OrderController.getOrderById); | ||||
| // router.post("/api/order/:id/cancel", OrderController.cancelOrder); | ||||
| // router.post("/api/order/:id/extend", OrderController.extendOrder); | ||||
|  | ||||
| router.post("/payment/stripe", StripePaymentController.create); | ||||
| router.post("/webhook/stripe", StripePaymentController.updatePayment); | ||||
|  | ||||
| // router.get("/api/payment/vipps/token", VippsTokenController); | ||||
| // router.get("/api/payment/:id/details", VippsPaymentController.getPaymentDetails); | ||||
| // router.post( | ||||
| //   "/api/payment/callback/v2/payments/:id", | ||||
| //   VippsPaymentController.updatePayment | ||||
| // ); | ||||
|  | ||||
| router.get("/", (req, res) => res.send("hello")); | ||||
|  | ||||
| app.use("/api", router); | ||||
|  | ||||
| const server = createServer(app); | ||||
| server.listen(port, () => logger.info(`Server started, listening at :${port}`)); | ||||
|  | ||||
| setupCartWebsocketServer(server); | ||||
							
								
								
									
										56
									
								
								src/webserver/websocketCartServer.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/webserver/websocketCartServer.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| import cookie from "cookie"; | ||||
| import { ClientRequest } from "http"; | ||||
| import { WebSocketServer, Websocket, Request } from "ws"; | ||||
| import WSCart from "../cart/WSCart"; | ||||
| import CartSession from "../cart/CartSession"; | ||||
| import generateUUID from "../utils/generateUUID"; | ||||
|  | ||||
| function getCookieValue(cookieString: string, key: string): string | null { | ||||
|   const cookies = cookie.parse(cookieString || ""); | ||||
|   return cookies[key] || null; | ||||
| } | ||||
|  | ||||
| function getHeaderValue(url: string, key: string) { | ||||
|   const urlSegments = url.split("?"); | ||||
|   if (urlSegments?.length < 2) return; | ||||
|  | ||||
|   const query = new URLSearchParams(urlSegments[1]); | ||||
|   return query.get(key); | ||||
| } | ||||
|  | ||||
| function setupCartWebsocketServer(server) { | ||||
|   const WS_OPTIONS = { server }; | ||||
|   const wss = new WebSocketServer(WS_OPTIONS); | ||||
|  | ||||
|   const cartSession = new CartSession(); | ||||
|   // setInterval(() => cartSession.listCarts(), 3000); | ||||
|   setInterval(() => cartSession.removeIfNotAlive(), 1000); | ||||
|  | ||||
|   wss.on("connection", (ws, req) => { | ||||
|     const sessionId = generateUUID(); | ||||
|     let clientId = | ||||
|       getCookieValue(req.headers.cookie, "planetId") || | ||||
|       getHeaderValue(req.url, "planetId"); | ||||
|  | ||||
|     if (clientId === null) return; | ||||
|  | ||||
|     const wsCart = new WSCart(ws, clientId); | ||||
|     wsCart.cartSession = cartSession; | ||||
|     cartSession.add(sessionId, wsCart); | ||||
|  | ||||
|     ws.on("message", (data, isBinary) => wsCart.handleMessage(data, isBinary)); | ||||
|  | ||||
|     ws.on("close", () => { | ||||
|       cartSession.remove(sessionId); | ||||
|       console.log("the client has closed the connection"); | ||||
|     }); | ||||
|  | ||||
|     ws.onerror = function (error) { | ||||
|       console.log("unexpected ws error occured:", error); | ||||
|     }; | ||||
|   }); | ||||
|  | ||||
|   console.log("Booted websocket cart"); | ||||
| } | ||||
|  | ||||
| export { setupCartWebsocketServer }; | ||||
		Reference in New Issue
	
	Block a user