Files
planetposen-backend/src/cart/WSCart.ts
Kevin a28b47413a Improvements to order, product, checkout & added shipping (#2)
* planet_id variable casing consistent with database field

Renames all clientId variables to planet_id

* Store stripe json response from latest payment & charge webhook

Also added db seed file for stripe payments

* Image support! Can now add, update & delete images from products

Images moved away from product schema to it's own table.
Accepts a images string and relates it to a product. Does not store the
images or verify the existance.

* Instead of deleting a product set field unlisted to true

Endpoints with current inventory checks for unlisted = false

* order_id & customer_no gets enhanced base64 function for generating id

* Implemented shipping for orders using Post tracking api

Added CRUD for making changes to a order's shippment.
Split shipping table into shipment_courier, shipment & shipment_event.

* Updated and add product & product_sku functions updated

* Cart increment funciton checks stock before updating

* Endpoint for getting product audit log using 91pluss trigger

Read more about usage here: https://wiki.postgresql.org/wiki/Audit_trigger_91plus

* On stripe charge successfull send email to user with planetposen-mail

* More product seed data, linting & formatting

* Log file at /var/log/planetposen_logs & rotate max 3 files 100MB each

This will prob throw a error if folder does not exist, run: `(sudo)
mkdir -p /var/log/planetposen_logs`.

* All endpoints now prefixed with /api/v1

* Linting
2022-12-29 19:25:39 +01:00

140 lines
3.5 KiB
TypeScript

import Cart from "./Cart";
import CartSession from "./CartSession";
import type ICart from "../interfaces/ICart";
import { IProduct } from "../interfaces/IProduct";
const cartSession = new CartSession();
class InvalidLineItemIdError extends Error {
statusCode: number;
constructor() {
const message = "Fant ikke produktet som ble lagt til";
super(message);
this.statusCode = 400;
}
}
function parseDataAsCartPayload(message: string): ICartPayload {
let json: ICartPayload = null;
try {
json = JSON.parse(message);
} catch {}
return json;
}
interface ICartPayload {
command: string;
message: string;
product_no?: number;
product_sku_no?: number;
quantity?: number;
lineitem_id?: number;
}
class WSCart {
ws: WebSocket;
planet_id: string | null;
cart: Cart;
cartSession: CartSession;
constructor(ws, planet_id) {
this.ws = ws;
this.planet_id = planet_id;
this.cart = new Cart(planet_id);
this.cartSession;
}
get isAlive() {
return this.ws.readyState === 1;
}
/* emitters */
message(message: string, success = true) {
this.ws.send(JSON.stringify({ message, success }));
}
async emitCart(cart: any[] | null = null) {
if (cart === null || cart?.length === 0) {
cart = await this.cart.get();
}
this.ws.send(JSON.stringify({ cart, success: true }));
}
/* handle known commands */
async addCartProduct(payload): Promise<boolean> {
const { product_no, product_sku_no, quantity } = payload;
if (!product_no || !quantity) {
// throw here?
this.message("Missing product_no or quantity", false);
}
await this.cart.add(product_no, product_sku_no, quantity);
return true;
}
async removeCartProduct(lineitem_id: number): Promise<boolean> {
if (isNaN(lineitem_id)) throw new InvalidLineItemIdError();
await this.cart.remove(lineitem_id);
return true;
}
async decrementProductInCart(lineitem_id: number): Promise<boolean> {
if (isNaN(lineitem_id)) throw new InvalidLineItemIdError();
await this.cart.decrement(lineitem_id);
return true;
}
async incrementProductInCart(lineitem_id: number): Promise<boolean> {
// TODO validate the quantity trying to be added here ??
if (isNaN(lineitem_id)) throw new InvalidLineItemIdError();
await this.cart.increment(lineitem_id);
return true;
}
/* main ws data/message handler */
async handleMessage(data: Buffer | string, isBinary: boolean) {
const dataMessage = isBinary ? String(data) : data.toString();
if (dataMessage === "heartbeat") return;
const payload = parseDataAsCartPayload(dataMessage);
const { command } = payload;
try {
let emitCart = false;
if (command === "cart") this.emitCart();
else if (command === "add") {
emitCart = await this.addCartProduct(payload);
} else if (command === "rm") {
emitCart = await this.removeCartProduct(payload?.lineitem_id);
} else if (command === "decrement") {
emitCart = await this.decrementProductInCart(payload?.lineitem_id);
} else if (command === "increment") {
emitCart = await this.incrementProductInCart(payload?.lineitem_id);
} else {
console.log(`client has sent us other/without command: ${dataMessage}`);
}
if (emitCart) {
this.cartSession.emitChangeToClients(this);
}
} catch (error) {
// ????
if (error.message) this.message(error?.message, false);
this.cartSession.emitChangeToClients(this);
}
}
handleError() {}
destroy() {}
}
export default WSCart;