import { Request, Response } from "express"; import logger, { storeLogger, userLogger } from "../logger/logger"; import { isEmailValid, isLanguageCodeValid, isPasswordValid, isUserIdValid, isUsernameValid, } from "../validator/validator"; import { decodeBase64, getUserSession, hashPassword, newUserId, } from "../utils/utils"; import User from "../models/user"; import { ACCOUNT_STATE, PAYMENT_PLAN_SETTINGS, Roles, USER_ANALYTICS_ENABLED_DEFAULT, } from "../utils/constants"; import Store from "../models/store"; import { isTerminPlanerGoogleCalendarConnected, terminPlanerRequest, terminPlanerRequestChangeFutureBookingDays, } from "../utils/terminPlaner"; export async function AddEmployee(req: Request, res: Response) { try { let { storeId, username, email, password, calendarMaxFutureBookingDays, calendarMinEarliestBookingTime, language, passwordSetOnInitLogging, } = req.body; // validate request if ( !storeId || !username || !email || passwordSetOnInitLogging === undefined || (!password && passwordSetOnInitLogging === false) || !language || !isLanguageCodeValid(language) ) { return res.status(400).send({ err: "invalid request" }); } // verify if requester is a store master const requesterSession = await getUserSession(req); if (!requesterSession) { return res.status(401).send({ err: "unauthorized" }); } const store = await Store.findOne({ where: { store_id: storeId, }, }); if (!store) { return res.status(400).send({ err: "invalid request" }); } if (store.owner_user_id !== requesterSession.user_id) { return res.status(401).send({ err: "unauthorized" }); } // get payment plan of store owner const storeOwner = await User.findOne({ where: { user_id: store.owner_user_id, }, attributes: ["payment_plan"], }); if (!storeOwner) { return res.status(400).send({ err: "invalid request" }); } // check max employees limit by payment plan const employees = await User.findAll({ where: { store_id: storeId, }, }); if ( employees.length - 1 >= PAYMENT_PLAN_SETTINGS[storeOwner.payment_plan].maxEmployees ) { return res.status(400).send({ err: "invalid request" }); } // validate username and email email = email.toLowerCase(); if (!isUsernameValid(username) || !(await isEmailValid(email))) { return res.status(400).send({ err: "invalid request" }); } // check if user already exists const existingUser = await User.findOne({ where: { email: email, }, }); if (existingUser) { return res.status(400).send({ err: "invalid request" }); } // create user const userId = newUserId(); let newUser = { user_id: userId, store_id: storeId, role: Roles.Worker, email: email, username: username, calendar_max_future_booking_days: calendarMaxFutureBookingDays, calendar_min_earliest_booking_time: calendarMinEarliestBookingTime, language: language, analytics_enabled: USER_ANALYTICS_ENABLED_DEFAULT, state: passwordSetOnInitLogging ? ACCOUNT_STATE.INIT_LOGIN : ACCOUNT_STATE.ACTIVE, payment_plan: storeOwner.payment_plan, }; if (!passwordSetOnInitLogging) { // decode password const decodedPassword = decodeBase64(password); if (!isPasswordValid(decodedPassword)) { return res.status(400).send({ err: "invalid request" }); } // hash password const hashedPassword = await hashPassword(decodedPassword); newUser = { ...newUser, password: hashedPassword, } as { user_id: string; store_id: any; role: string; email: any; username: any; calendar_max_future_booking_days: any; calendar_min_earliest_booking_time: any; language: any; analytics_enabled: boolean; password: string; state: number; payment_plan: number; }; } await User.create(newUser); const googleCalendarConnected = await isTerminPlanerGoogleCalendarConnected( store.owner_user_id ); // only request terminplaner if google calendar is connected if (googleCalendarConnected) { try { await terminPlanerRequest("/api/v1/addUser", "POST", { userId: userId, }); storeLogger.info( storeId, "Added employee with email:", email, "username:", username, "passwordSetOnInitLogging:", passwordSetOnInitLogging ); return res.status(200).send({ msg: "success" }); } catch (err) { // remove user from database if terminplaner request failed await User.destroy({ where: { user_id: userId, }, }); logger.error("terminPlanerRequest err on add employee:", err); return res.status(500).send({ err: "invalid request" }); } } storeLogger.info( storeId, "Added employee with email:", email, "username:", username, "passwordSetOnInitLogging:", passwordSetOnInitLogging ); res.status(200).send({ msg: "success" }); } catch (error) { logger.error(error); res.status(500).send({ err: "invalid request" }); } } export async function GetEmployees(req: Request, res: Response) { try { let { storeId } = req.params; const requesterSession = await getUserSession(req); if (!requesterSession) { logger.debug("Requester session not found"); return res.status(401).send({ err: "unauthorized" }); } // verify if requester is a store master const store = await Store.findOne({ where: { store_id: storeId, }, attributes: [ "owner_user_id", "calendar_max_future_booking_days", "calendar_min_earliest_booking_time", ], }); if (!store) { logger.debug("Store not found"); return res.status(400).send({ err: "invalid request" }); } if (store.owner_user_id !== requesterSession.user_id) { logger.debug("Requester is not the store owner"); return res.status(401).send({ err: "unauthorized" }); } // find all employees of the requester const employees = await User.findAll({ where: { store_id: storeId, }, attributes: [ "user_id", "username", "email", "calendar_max_future_booking_days", "calendar_min_earliest_booking_time", ], }); // filter out the requester from the employees const filteredEmployees = employees.filter( (employee) => employee.user_id !== requesterSession.user_id ); userLogger.info(requesterSession.user_id, "GetEmployees"); res.status(200).send({ storeSettings: { calendar_max_future_booking_days: store.calendar_max_future_booking_days, calendar_min_earliest_booking_time: store.calendar_min_earliest_booking_time, }, employees: filteredEmployees, }); } catch (error) { logger.error(error); res.status(500).send({ err: "invalid request" }); } } export async function UpdateEmployee(req: Request, res: Response) { try { let { userId, username, email, calendarMaxFutureBookingDays, calendarMinEarliestBookingTime, } = req.body; if (!isUserIdValid(userId)) { return res.status(400).send({ err: "invalid request" }); } // verify if requester is a store master const requesterSession = await getUserSession(req); if (!requesterSession) { return res.status(401).send({ err: "unauthorized" }); } const store = await Store.findOne({ where: { owner_user_id: requesterSession.user_id, }, attributes: ["store_id"], }); if (!store) { return res.status(400).send({ err: "invalid request" }); } // check if user exists const existingUser = await User.findOne({ where: { user_id: userId, }, }); if (!existingUser) { return res.status(400).send({ err: "invalid request" }); } // update data let update = {}; if (email) { email = email.toLowerCase(); if (!(await isEmailValid(email))) { res.status(400).send({ err: "invalid request" }); return; } update = { ...update, email: email, }; } if (username) { if (!isUsernameValid(username)) { res.status(400).send({ err: "invalid request" }); return; } update = { ...update, username: username, }; } if (calendarMaxFutureBookingDays) { update = { ...update, calendar_max_future_booking_days: calendarMaxFutureBookingDays, }; } if (calendarMinEarliestBookingTime) { update = { ...update, calendar_min_earliest_booking_time: calendarMinEarliestBookingTime, }; await terminPlanerRequestChangeFutureBookingDays(store.store_id); } if (Object.keys(update).length === 0) { return res.status(400).send({ err: "invalid request" }); } // update user await User.update(update, { where: { user_id: userId, }, }); storeLogger.info( store.store_id, "Updated employee with user id:", userId, "username:", username, "email:", email ); res.status(200).send({ msg: "success" }); } catch (error) { logger.error(error); res.status(500).send({ err: "invalid request" }); } } export async function DeleteEmployee(req: Request, res: Response) { try { let { userId } = req.body; // validate request if (!userId) { return res.status(400).send({ err: "invalid request" }); } // verify if requester is a store master const requesterSession = await getUserSession(req); if (!requesterSession) { return res.status(401).send({ err: "unauthorized" }); } const store = await Store.findOne({ where: { owner_user_id: requesterSession.user_id, }, attributes: ["store_id", "owner_user_id"], }); if (!store) { return res.status(400).send({ err: "invalid request" }); } // check if user exists const existingUser = await User.findOne({ where: { user_id: userId, }, }); if (!existingUser) { return res.status(400).send({ err: "invalid request" }); } const googleCalendarConnected = await isTerminPlanerGoogleCalendarConnected( store.owner_user_id ); // only request terminplaner if google calendar is connected if (googleCalendarConnected) { await terminPlanerRequest("/api/v1/removeUser", "POST", { userId: userId, }); } await User.destroy({ where: { user_id: userId, }, }); storeLogger.info(store.store_id, "Deleted employee with user id:", userId); res.status(200).send({ msg: "success" }); } catch (error) { logger.error(error); res.status(500).send({ err: "invalid request" }); } }