main
alex 2024-01-13 12:07:00 +01:00
parent 366e3f3b23
commit da0814bcf5
18 changed files with 1074 additions and 48 deletions

View File

@ -4,6 +4,8 @@ import bodyParser from "body-parser";
import swaggerUI from "swagger-ui-express";
import cors from "cors";
import storeRoutes from "./src/routes/storeRoutes";
import storeServicesRoutes from "./src/routes/storeServicesRoutes";
import userRoutes from "./src/routes/userRoutes";
import usersRoutes from "./src/routes/usersRoutes";
@ -44,6 +46,8 @@ const options = {
// TODO: add cors
app.use(cors());
app.use(bodyParser.json());
app.use("/api/v1/store", storeRoutes);
app.use("/api/v1/store/services", storeServicesRoutes);
app.use("/api/v1/user", userRoutes);
app.use("/api/v1/users", usersRoutes);

View File

@ -0,0 +1,41 @@
import { Request, Response } from "express";
import Store from "../models/store";
import { getUserSession } from "../utils/utils";
import logger from "../logger/logger";
export async function GetStore(req: Request, res: Response) {
try {
const { storeId } = req.params;
if (!storeId) {
return res.status(400).send({ err: "invalid request" });
}
// check if requester is the store owner
const userSession = await getUserSession(req);
if (!userSession) {
return res.status(401).send({ err: "unauthorized" });
}
const store = await Store.findOne({
where: {
store_id: storeId,
},
attributes: ["owner_user_id", "name"],
});
if (!store) {
return res.status(400).send({ err: "invalid request" });
}
if (store.owner_user_id !== userSession.user_id) {
return res.status(401).send({ err: "unauthorized" });
}
res.status(200).send({ store: { name: store.name } });
} catch (error) {
res.status(500).send({ err: "invalid request" });
}
}

View File

@ -0,0 +1,338 @@
import { Request, Response } from "express";
import StoreService from "../models/storeService";
import {
getUserSession,
newStoreServiceActivityId,
newStoreServiceId,
} from "../utils/utils";
import {
isStoreServiceActivityDescriptionValid,
isStoreServiceActivityDurationValid,
isStoreServiceActivityNameValid,
isStoreServiceActivityPriceValid,
isStoreServiceNameValid,
} from "../validator/validator";
import Store from "../models/store";
import logger from "../logger/logger";
import StoreServiceActivity from "../models/storeServiceActivity";
import StoreServiceActivityUsers from "../models/storeServiceActivityUsers";
import User from "../models/user";
export async function GetStoreServices(req: Request, res: Response) {
try {
const { storeId } = req.params;
if (!storeId) {
logger.debug("Invalid request");
return res.status(400).send({ err: "invalid request" });
}
const services = await StoreService.findAll({
where: {
store_id: storeId,
},
attributes: ["service_id", "name"],
});
// get all users assigned to the store
// this user list will be used in the ui to show if no users are selected for a service activity
const users = await User.findAll({
where: {
store_id: storeId,
},
attributes: ["user_id", "username"],
});
res.status(200).send({ services, users });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function CreateStoreService(req: Request, res: Response) {
try {
const { storeId, name } = req.body;
if (!storeId || !name || !isStoreServiceNameValid(name)) {
logger.debug("Invalid request");
return res.status(400).send({ err: "invalid request" });
}
// check if requester is the store owner
const userSession = await getUserSession(req);
if (!userSession) {
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 !== userSession.user_id) {
return res.status(401).send({ err: "unauthorized" });
}
// create store service
const service = await StoreService.create({
service_id: newStoreServiceId(),
store_id: storeId,
name: name,
});
res.status(200).send({ service });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function UpdateStoreService(req: Request, res: Response) {
try {
const { serviceId, name } = req.body;
if (!serviceId || !name || !isStoreServiceNameValid(name)) {
logger.debug("Invalid request");
return res.status(400).send({ err: "invalid request" });
}
// check if requester is the store owner
const userSession = await getUserSession(req);
if (!userSession) {
return res.status(401).send({ err: "unauthorized" });
}
const service = await StoreService.findOne({
where: {
service_id: serviceId,
},
});
if (!service) {
return res.status(400).send({ err: "invalid request" });
}
const store = await Store.findOne({
where: {
store_id: service.store_id,
},
});
if (!store) {
return res.status(400).send({ err: "invalid request" });
}
if (store.owner_user_id !== userSession.user_id) {
return res.status(401).send({ err: "unauthorized" });
}
// update store service
await StoreService.update(
{
name: name,
},
{
where: {
service_id: serviceId,
},
}
);
res.status(200).send({ msg: "success" });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function CreateStoreServiceActivity(req: Request, res: Response) {
try {
const { serviceId, name, description, price, duration } = req.body;
if (
!serviceId ||
!name ||
!isStoreServiceActivityNameValid(name) ||
!isStoreServiceActivityDescriptionValid(description) ||
!isStoreServiceActivityPriceValid(price) ||
!isStoreServiceActivityDurationValid(duration)
) {
logger.debug("Invalid request");
return res.status(400).send({ err: "invalid request" });
}
// check if requester is the store owner
const userSession = await getUserSession(req);
if (!userSession) {
return res.status(401).send({ err: "unauthorized" });
}
const service = await StoreService.findOne({
where: {
service_id: serviceId,
},
});
if (!service) {
return res.status(400).send({ err: "invalid request" });
}
const store = await Store.findOne({
where: {
store_id: service.store_id,
},
});
if (!store) {
return res.status(400).send({ err: "invalid request" });
}
if (store.owner_user_id !== userSession.user_id) {
return res.status(401).send({ err: "unauthorized" });
}
// create store service activity
const activity = await StoreServiceActivity.create({
activity_id: newStoreServiceActivityId(),
service_id: serviceId,
name: name,
description: description,
price: price,
duration: duration,
});
res.status(200).send({ activity });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function GetStoreServiceActivities(req: Request, res: Response) {
try {
const { storeId, serviceId } = req.params;
if (!storeId || !serviceId) {
return res.status(400).send({ err: "invalid request" });
}
const activities = await StoreServiceActivity.findAll({
where: {
service_id: serviceId,
},
attributes: ["activity_id", "name", "description", "price", "duration"],
});
/*
activities.forEach(async (activity) => {
const assignedUsers = await StoreServiceActivityUsers.findAll({
where: {
activity_id: activity.activity_id,
},
attributes: ["user_id"],
});
}); */
res.status(200).send({ activities });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function UpdateStoreServiceActivity(req: Request, res: Response) {
try {
const { activityId, name, description, price, duration } = req.body;
if (
!activityId ||
!name ||
!isStoreServiceActivityNameValid(name) ||
!isStoreServiceActivityDescriptionValid(description) ||
!isStoreServiceActivityPriceValid(price) ||
!isStoreServiceActivityDurationValid(duration)
) {
logger.debug("Invalid request");
return res.status(400).send({ err: "invalid request" });
}
// check if requester is the store owner
const userSession = await getUserSession(req);
if (!userSession) {
return res.status(401).send({ err: "unauthorized" });
}
const activity = await StoreServiceActivity.findOne({
where: {
activity_id: activityId,
},
});
if (!activity) {
return res.status(400).send({ err: "invalid request" });
}
const service = await StoreService.findOne({
where: {
service_id: activity.service_id,
},
});
if (!service) {
return res.status(400).send({ err: "invalid request" });
}
const store = await Store.findOne({
where: {
store_id: service.store_id,
},
});
if (!store) {
return res.status(400).send({ err: "invalid request" });
}
if (store.owner_user_id !== userSession.user_id) {
return res.status(401).send({ err: "unauthorized" });
}
// update store service activity
await StoreServiceActivity.update(
{
name: name,
description: description,
price: price,
duration: duration,
},
{
where: {
activity_id: activityId,
},
}
);
res.status(200).send({ msg: "success" });
} catch (error) {
console.log(error);
res.status(500).send({ err: "invalid request" });
}
}

View File

@ -6,15 +6,24 @@ import {
isPasswordValid,
isUsernameValid,
} from "../validator/validator";
import { Roles } from "../utils/constants";
import {
CALENDAR_MAX_FUTURE_BOOKING_DAYS,
CALENDAR_MAX_SERVICE_DURATION,
CALENDAR_MIN_EARLIEST_BOOKING_TIME,
CALENDAR_PRIMARY_CALENDAR_ID,
CALENDAR_USING_PRIMARY_CALENDAR,
Roles,
} from "../utils/constants";
import {
decodeBase64,
getUserSession,
hashPassword,
matchPassword,
newStoreId,
newUserId,
saveSession,
} from "../utils/utils";
import Store from "../models/store";
export async function SignUp(req: Request, res: Response) {
try {
@ -65,24 +74,51 @@ export async function SignUp(req: Request, res: Response) {
const hashedPassword = await hashPassword(decodedPassword);
// create user
// create store
await User.create({
user_id: newUserId(),
role: Roles.Master,
account_name: accountName,
username: username,
password: hashedPassword,
let userId = newUserId();
Store.create({
store_id: newStoreId(),
owner_user_id: userId,
name: username,
calendar_max_future_booking_days: CALENDAR_MAX_FUTURE_BOOKING_DAYS,
calendar_min_earliest_booking_time: CALENDAR_MIN_EARLIEST_BOOKING_TIME,
calendar_primary_calendar_id: CALENDAR_PRIMARY_CALENDAR_ID,
calendar_using_primary_calendar: CALENDAR_USING_PRIMARY_CALENDAR,
calendar_max_service_duration: CALENDAR_MAX_SERVICE_DURATION,
})
.then((user) => {
.then(async (store) => {
logger.debug(
"User created with accountName: %s username: %s",
user.account_name,
user.username
"Store created with storeId: %s storeName: %s",
store.store_id,
store.name
);
// create session
saveSession(res, user.user_id, user.username);
// create user
await User.create({
user_id: userId,
store_id: store.store_id,
role: Roles.Master,
account_name: accountName,
username: username,
password: hashedPassword,
})
.then((user) => {
logger.debug(
"User created with accountName: %s username: %s",
user.account_name,
user.username
);
// create session
saveSession(res, user.user_id, user.username);
})
.catch((err) => {
logger.error(err);
res.status(500).send({ err: "invalid request" });
});
})
.catch((err) => {
logger.error(err);
@ -180,13 +216,60 @@ export async function GetUser(req: Request, res: Response) {
where: {
user_id: session.user_id,
},
attributes: ["user_id", "username"],
});
if (!user) {
return res.status(401).send({ err: "unauthorized" });
}
res.status(200).send({ username: user.username });
const stores = await Store.findAll({
where: {
owner_user_id: user.user_id,
},
attributes: ["store_id", "name"],
});
res.status(200).send({ user, stores });
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function IsAccountNameAvailable(req: Request, res: Response) {
try {
let { accountName } = req.body;
// validate request
if (!accountName) {
return res.status(400).send({ err: "invalid request" });
}
accountName = accountName.toLowerCase();
if (!isAccountNameValid(accountName)) {
return res.status(400).send({ err: "invalid request" });
}
// check if user exists
const user = await User.findOne({
where: {
account_name: accountName,
},
});
if (user) {
logger.debug(
"User already exists with this accountName: %s",
accountName
);
return res.status(400).send({ err: "invalid request" });
}
res.status(200).send({ msg: "account name available" });
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });

View File

@ -3,6 +3,7 @@ import logger from "../logger/logger";
import {
isAccountNameValid,
isPasswordValid,
isUserIdValid,
isUsernameValid,
} from "../validator/validator";
import {
@ -16,11 +17,11 @@ import { Roles } from "../utils/constants";
export async function AddEmployee(req: Request, res: Response) {
try {
let { username, accountName, password } = req.body;
let { storeId, username, accountName, password } = req.body;
// validate request
if (!username || !accountName || !password) {
if (!storeId || !username || !accountName || !password) {
return res.status(400).send({ err: "invalid request" });
}
@ -53,11 +54,10 @@ export async function AddEmployee(req: Request, res: Response) {
accountName = accountName.toLowerCase();
if (!isUsernameValid(username)) {
return res.status(400).send({ err: "invalid request" });
}
if (!isAccountNameValid(accountName)) {
if (
!isUsernameValid(username) ||
!(await isAccountNameValid(accountName))
) {
return res.status(400).send({ err: "invalid request" });
}
@ -94,11 +94,11 @@ export async function AddEmployee(req: Request, res: Response) {
await User.create({
user_id: newUserId(),
store_id: storeId,
role: Roles.Worker,
account_name: accountName,
username: username,
password: hashedPassword,
master_user_id: requester.user_id,
})
.then(() => {
res.status(200).send({ msg: "success" });
@ -115,6 +115,8 @@ export async function AddEmployee(req: Request, res: Response) {
export async function GetEmployees(req: Request, res: Response) {
try {
let { storeId } = req.params;
const requesterSession = await getUserSession(req);
if (!requesterSession) {
@ -126,18 +128,191 @@ export async function GetEmployees(req: Request, res: Response) {
const employees = await User.findAll({
where: {
master_user_id: requesterSession.user_id,
store_id: storeId,
},
attributes: ["username", "account_name"],
attributes: ["user_id", "username", "account_name"],
});
// simulate a delay
await new Promise((resolve) => setTimeout(resolve, 4000));
res.status(200).send({ employees: employees });
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
}
}
export async function UpdateEmployee(req: Request, res: Response) {
try {
let { userId, username, accountName } = req.body;
// validate request
// check if username or account name is provided
if (!userId || (!username && !accountName)) {
return res.status(400).send({ err: "invalid request" });
}
// validate username and account name
if (!isUserIdValid(userId)) {
return res.status(400).send({ err: "invalid request" });
}
let update = {};
if (accountName) {
accountName = accountName.toLowerCase();
if (!(await isAccountNameValid(accountName))) {
res.status(400).send({ err: "invalid request" });
return;
}
update = {
...update,
account_name: accountName,
};
}
if (username) {
if (!isUsernameValid(username)) {
res.status(400).send({ err: "invalid request" });
return;
}
update = {
...update,
username: username,
};
}
// verify if requester is a store master
const requesterSession = await getUserSession(req);
if (!requesterSession) {
logger.debug("Requester session not found");
return res.status(401).send({ err: "unauthorized" });
}
const requester = await User.findOne({
where: {
user_id: requesterSession.user_id,
},
});
if (!requester) {
logger.debug("Requester not found");
return res.status(401).send({ err: "unauthorized" });
}
if (requester.role !== Roles.Master) {
logger.debug("Requester is not a store master");
return res.status(401).send({ err: "unauthorized" });
}
// check if user exists
const existingUser = await User.findOne({
where: {
user_id: userId,
},
});
if (!existingUser) {
logger.debug("User not found");
return res.status(400).send({ err: "invalid request" });
}
// update user
await User.update(
{
username: username,
account_name: accountName,
},
{
where: {
user_id: userId,
},
}
)
.then(() => {
res.status(200).send({ msg: "success" });
})
.catch((err) => {
logger.error(err);
res.status(500).send({ err: "invalid request" });
});
} 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) {
logger.debug("Requester session not found");
return res.status(401).send({ err: "unauthorized" });
}
const requester = await User.findOne({
where: {
user_id: requesterSession.user_id,
},
});
if (!requester) {
logger.debug("Requester not found");
return res.status(401).send({ err: "unauthorized" });
}
if (requester.role !== Roles.Master) {
logger.debug("Requester is not a store master");
return res.status(401).send({ err: "unauthorized" });
}
// check if user exists
const existingUser = await User.findOne({
where: {
user_id: userId,
},
});
if (!existingUser) {
logger.debug("User not found");
return res.status(400).send({ err: "invalid request" });
}
// delete user
await User.destroy({
where: {
user_id: userId,
},
})
.then(() => {
res.status(200).send({ msg: "success" });
})
.catch((err) => {
logger.error(err);
res.status(500).send({ err: "invalid request" });
});
} catch (error) {
logger.error(error);
res.status(500).send({ err: "invalid request" });
}
}

View File

@ -1,9 +1,18 @@
import Session from "./session";
import Store from "./store";
import StoreService from "./storeService";
import StoreServiceActivity from "./storeServiceActivity";
import StoreServiceActivityUsers from "./storeServiceActivityUsers";
import User from "./user";
function syncModels() {
User.sync({ alter: true });
Session.sync({ alter: true });
User.sync();
Session.sync();
Store.sync();
StoreService.sync();
StoreServiceActivity.sync();
StoreServiceActivityUsers.sync();
}
export default syncModels;

69
src/models/store.ts Normal file
View File

@ -0,0 +1,69 @@
import { DataTypes, Model } from "sequelize";
import sequelize from "../database/database";
interface StoreAttributes {
store_id: string;
owner_user_id: string;
name: string;
calendar_max_future_booking_days: number;
calendar_min_earliest_booking_time: number;
calendar_primary_calendar_id: string;
calendar_using_primary_calendar: boolean;
calendar_max_service_duration: number;
}
class Store extends Model<StoreAttributes> implements StoreAttributes {
declare store_id: string;
declare owner_user_id: string;
declare name: string;
declare calendar_max_future_booking_days: number;
declare calendar_min_earliest_booking_time: number;
declare calendar_primary_calendar_id: string;
declare calendar_using_primary_calendar: boolean;
declare calendar_max_service_duration: number;
}
Store.init(
{
// Model attributes are defined here
store_id: {
primaryKey: true,
type: DataTypes.STRING,
allowNull: false,
},
owner_user_id: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
calendar_max_future_booking_days: {
type: DataTypes.INTEGER,
allowNull: false,
},
calendar_min_earliest_booking_time: {
type: DataTypes.INTEGER,
allowNull: false,
},
calendar_primary_calendar_id: {
type: DataTypes.STRING,
allowNull: false,
},
calendar_using_primary_calendar: {
type: DataTypes.BOOLEAN,
allowNull: false,
},
calendar_max_service_duration: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
tableName: "stores",
sequelize, // passing the `sequelize` instance is required
}
);
export default Store;

View File

@ -0,0 +1,42 @@
import { DataTypes, Model } from "sequelize";
import sequelize from "../database/database";
interface StoreServiceAttributes {
service_id: string;
store_id: string;
name: string;
}
class StoreService
extends Model<StoreServiceAttributes>
implements StoreServiceAttributes
{
declare service_id: string;
declare store_id: string;
declare name: string;
}
StoreService.init(
{
// Model attributes are defined here
service_id: {
primaryKey: true,
type: DataTypes.STRING,
allowNull: false,
},
store_id: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
tableName: "store_services",
sequelize, // passing the `sequelize` instance is required
}
);
export default StoreService;

View File

@ -0,0 +1,60 @@
import { DataTypes, Model } from "sequelize";
import sequelize from "../database/database";
interface StoreServiceActivityAttributes {
activity_id: string;
service_id: string;
name: string;
description: string;
price: number;
duration: number;
}
class StoreServiceActivity
extends Model<StoreServiceActivityAttributes>
implements StoreServiceActivityAttributes
{
declare activity_id: string;
declare service_id: string;
declare name: string;
declare description: string;
declare price: number;
declare duration: number;
}
StoreServiceActivity.init(
{
// Model attributes are defined here
activity_id: {
primaryKey: true,
type: DataTypes.STRING,
allowNull: false,
},
service_id: {
type: DataTypes.STRING,
allowNull: false,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
description: {
type: DataTypes.TEXT,
allowNull: false,
},
price: {
type: DataTypes.FLOAT,
allowNull: false,
},
duration: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
tableName: "store_service_activities",
sequelize, // passing the `sequelize` instance is required
}
);
export default StoreServiceActivity;

View File

@ -0,0 +1,37 @@
import { DataTypes, Model } from "sequelize";
import sequelize from "../database/database";
interface StoreServiceActivityUsersAttributes {
activity_id: string;
user_id: string;
}
class StoreServiceActivityUsers
extends Model<StoreServiceActivityUsersAttributes>
implements StoreServiceActivityUsersAttributes
{
declare activity_id: string;
declare user_id: string;
}
StoreServiceActivityUsers.init(
{
// Model attributes are defined here
activity_id: {
primaryKey: true,
type: DataTypes.STRING,
allowNull: false,
},
user_id: {
primaryKey: true,
type: DataTypes.STRING,
allowNull: false,
},
},
{
tableName: "store_service_activity_users",
sequelize, // passing the `sequelize` instance is required
}
);
export default StoreServiceActivityUsers;

View File

@ -3,22 +3,29 @@ import sequelize from "../database/database";
interface UserAttributes {
user_id: string;
master_user_id?: string;
store_id: string;
// TODO: change to role_id
role: string;
account_name: string;
username: string;
password: string;
calendar_settings?: string;
calendar_max_future_booking_days?: number;
calendar_min_earliest_booking_time?: number;
calendar_primary_calendar_id?: string;
calendar_using_primary_calendar?: boolean;
}
class User extends Model<UserAttributes> implements UserAttributes {
declare user_id: string;
declare master_user_id: string;
declare store_id: string;
declare role: string;
declare account_name: string;
declare username: string;
declare password: string;
declare calendar_settings: string;
declare calendar_max_future_booking_days: number;
declare calendar_min_earliest_booking_time: number;
declare calendar_primary_calendar_id: string;
declare calendar_using_primary_calendar: boolean;
}
User.init(
@ -29,13 +36,13 @@ User.init(
type: DataTypes.STRING,
allowNull: false,
},
master_user_id: {
store_id: {
type: DataTypes.STRING,
// allowNull defaults to true
allowNull: false,
},
role: {
type: DataTypes.STRING,
// allowNull defaults to true
allowNull: false,
},
account_name: {
type: DataTypes.STRING,
@ -49,9 +56,17 @@ User.init(
type: DataTypes.STRING,
allowNull: false,
},
calendar_settings: {
calendar_max_future_booking_days: {
type: DataTypes.INTEGER,
},
calendar_min_earliest_booking_time: {
type: DataTypes.INTEGER,
},
calendar_primary_calendar_id: {
type: DataTypes.STRING,
// allowNull defaults to true
},
calendar_using_primary_calendar: {
type: DataTypes.BOOLEAN,
},
},
{

View File

@ -0,0 +1,8 @@
import express from "express";
const router = express.Router();
import * as storeController from "../controllers/storeController";
router.get("/:storeId", storeController.GetStore);
export default router;

View File

@ -0,0 +1,19 @@
import express from "express";
const router = express.Router();
import * as storeServicesController from "../controllers/storeServicesController";
router.get("/:storeId", storeServicesController.GetStoreServices);
router.post("/", storeServicesController.CreateStoreService);
router.post("/update", storeServicesController.UpdateStoreService);
router.post("/activity", storeServicesController.CreateStoreServiceActivity);
router.get(
"/activities/:storeId/:serviceId",
storeServicesController.GetStoreServiceActivities
);
router.post(
"/activity/update",
storeServicesController.UpdateStoreServiceActivity
);
export default router;

View File

@ -8,5 +8,6 @@ router.post("/auth/signup", userController.SignUp);
router.post("/auth/login", userController.Login);
router.delete("/auth/logout", sessionProtection, userController.Logout);
router.get("/", sessionProtection, userController.GetUser);
router.post("/auth/check/accountname", userController.IsAccountNameAvailable);
export default router;

View File

@ -4,6 +4,8 @@ const router = express.Router();
import * as usersController from "../controllers/usersController";
router.post("/", usersController.AddEmployee);
router.get("/", usersController.GetEmployees);
router.get("/:storeId", usersController.GetEmployees);
router.post("/update", usersController.UpdateEmployee);
router.delete("/", usersController.DeleteEmployee);
export default router;

View File

@ -1,4 +1,5 @@
export const DEFAULT_SESSION_EXPIRY = 365 * 24 * 60 * 60 * 1000; // 365 days
export const USER_SESSION_LENGTH = 32;
export const USERNAME_MIN_LENGTH = 3;
export const USERNAME_MAX_LENGTH = 20;
@ -9,9 +10,36 @@ export const ACCOUNT_NAME_MAX_LENGTH = 20;
export const PASSWORD_MIN_LENGTH = 8;
export const PASSWORD_MAX_LENGTH = 64;
export const USER_ID_LENGTH = 36;
// Header name for the session ID
export const HEADER_X_AUTHORIZATION: string = "x-authorization";
export const STORE_SERVICE_MIN_LENGTH = 3;
export const STORE_SERVICE_MAX_LENGTH = 32;
export const STORE_SERVICE_ACTIVITY_NAME_MIN_LENGTH = 3;
export const STORE_SERVICE_ACTIVITY_NAME_MAX_LENGTH = 32;
export const STORE_SERVICE_ACTIVITY_DESCRIPTION_MIN_LENGTH = 3;
export const STORE_SERVICE_ACTIVITY_DESCRIPTION_MAX_LENGTH = 1024;
export const STORE_SERVICE_ACTIVITY_PRICE_MIN = 0;
export const STORE_SERVICE_ACTIVITY_PRICE_MAX = 10000000;
export const STORE_SERVICE_ACTIVITY_DURATION_HOURS_MAX = 23;
export const STORE_SERVICE_ACTIVITY_DURATION_MINUTES_MAX = 60;
export const STORE_SERVICE_ACTIVITY_DURATION_MIN = 0;
export const STORE_SERVICE_ACTIVITY_DURATION_MAX =
STORE_SERVICE_ACTIVITY_DURATION_HOURS_MAX * 60 +
STORE_SERVICE_ACTIVITY_DURATION_MINUTES_MAX;
export const CALENDAR_MAX_FUTURE_BOOKING_DAYS = 14;
export const CALENDAR_MIN_EARLIEST_BOOKING_TIME = 1;
export const CALENDAR_PRIMARY_CALENDAR_ID = "";
export const CALENDAR_USING_PRIMARY_CALENDAR = false;
export const CALENDAR_MAX_SERVICE_DURATION = 1440; // 24 hours in minutes
export const Roles = {
// admin of the whole system independent of stores
Admin: "admin",

View File

@ -3,7 +3,11 @@ import bcrypt from "bcrypt";
import { v4 as uuidv4 } from "uuid";
import { Request, Response } from "express";
import Session from "../models/session";
import { DEFAULT_SESSION_EXPIRY, HEADER_X_AUTHORIZATION } from "./constants";
import {
DEFAULT_SESSION_EXPIRY,
HEADER_X_AUTHORIZATION,
USER_SESSION_LENGTH,
} from "./constants";
export async function matchPassword(decodedPassword: string, password: string) {
return await bcrypt.compare(decodedPassword, password);
@ -21,8 +25,20 @@ export function newUserId() {
return uuidv4();
}
export function newStoreId() {
return uuidv4();
}
export function newStoreServiceId() {
return uuidv4();
}
export function newStoreServiceActivityId() {
return uuidv4();
}
export function newUserSession() {
return crypto.randomBytes(32).toString("hex");
return crypto.randomBytes(USER_SESSION_LENGTH).toString("hex");
}
export async function saveSession(

View File

@ -1,3 +1,4 @@
import logger from "../logger/logger";
import {
USERNAME_MIN_LENGTH,
USERNAME_MAX_LENGTH,
@ -5,7 +6,19 @@ import {
PASSWORD_MAX_LENGTH,
ACCOUNT_NAME_MIN_LENGTH,
ACCOUNT_NAME_MAX_LENGTH,
USER_ID_LENGTH,
STORE_SERVICE_MIN_LENGTH,
STORE_SERVICE_MAX_LENGTH,
STORE_SERVICE_ACTIVITY_NAME_MAX_LENGTH,
STORE_SERVICE_ACTIVITY_NAME_MIN_LENGTH,
STORE_SERVICE_ACTIVITY_DESCRIPTION_MAX_LENGTH,
STORE_SERVICE_ACTIVITY_DESCRIPTION_MIN_LENGTH,
STORE_SERVICE_ACTIVITY_PRICE_MAX,
STORE_SERVICE_ACTIVITY_PRICE_MIN,
STORE_SERVICE_ACTIVITY_DURATION_MAX,
STORE_SERVICE_ACTIVITY_DURATION_MIN,
} from "../utils/constants";
import User from "../models/user";
// TODO: regex for username
export function isUsernameValid(username: string) {
@ -16,11 +29,28 @@ export function isUsernameValid(username: string) {
}
// TODO: regex for account name
export function isAccountNameValid(accountName: string) {
return (
accountName.length >= ACCOUNT_NAME_MIN_LENGTH &&
accountName.length <= ACCOUNT_NAME_MAX_LENGTH
);
export async function isAccountNameValid(accountName: string) {
if (
accountName.length < ACCOUNT_NAME_MIN_LENGTH ||
accountName.length > ACCOUNT_NAME_MAX_LENGTH
) {
return false;
}
// check if account name is already taken in the database
const existingUser = await User.findOne({
where: {
account_name: accountName,
},
});
if (existingUser) {
logger.debug("User already exists with this accountName: %s", accountName);
return false;
}
return true;
}
// TODO: regex for password
@ -30,3 +60,52 @@ export function isPasswordValid(password: string) {
password.length <= PASSWORD_MAX_LENGTH
);
}
export function isUserIdValid(userId: string) {
return userId.length === USER_ID_LENGTH;
}
export function isStoreServiceNameValid(storeServiceName: string) {
return (
storeServiceName.length >= STORE_SERVICE_MIN_LENGTH &&
storeServiceName.length <= STORE_SERVICE_MAX_LENGTH
);
}
export function isStoreServiceActivityNameValid(
storeServiceActivityName: string
) {
return (
storeServiceActivityName.length >= STORE_SERVICE_ACTIVITY_NAME_MIN_LENGTH &&
storeServiceActivityName.length <= STORE_SERVICE_ACTIVITY_NAME_MAX_LENGTH
);
}
export function isStoreServiceActivityDescriptionValid(
storeServiceActivityDescription: string
) {
return (
storeServiceActivityDescription.length >=
STORE_SERVICE_ACTIVITY_DESCRIPTION_MIN_LENGTH &&
storeServiceActivityDescription.length <=
STORE_SERVICE_ACTIVITY_DESCRIPTION_MAX_LENGTH
);
}
export function isStoreServiceActivityPriceValid(
storeServiceActivityPrice: any
) {
return (
storeServiceActivityPrice >= STORE_SERVICE_ACTIVITY_PRICE_MIN &&
storeServiceActivityPrice <= STORE_SERVICE_ACTIVITY_PRICE_MAX
);
}
export function isStoreServiceActivityDurationValid(
storeServiceActivityDuration: any
) {
return (
storeServiceActivityDuration >= STORE_SERVICE_ACTIVITY_DURATION_MIN &&
storeServiceActivityDuration <= STORE_SERVICE_ACTIVITY_DURATION_MAX
);
}