refactor: edit tailwindcss classes and change code formatting, therefore added .prettierrc file

fix: frontend bug where the passwords won't be checked when the password is changed
This commit is contained in:
2026-07-08 14:56:43 +02:00
parent 9c50c3f300
commit 6be34c93aa
36 changed files with 1280 additions and 1145 deletions
+12 -1
View File
@@ -2,34 +2,45 @@ FROM node:22-alpine AS builder
WORKDIR /app
# Workspace manifests (npm workspaces need these present)
COPY package.json package-lock.json ./
COPY backend/package.json backend/
COPY frontend/package.json frontend/
COPY shared/package.json shared/
# Install all deps for the monorepo (includes devDeps for building)
RUN npm ci
# Copy sources required to build shared + backend
COPY shared/ shared/
COPY backend/ backend/
# Build shared (now produces dist/esm + dist/cjs + dist/types) and then backend
RUN npm run build --workspace=shared
RUN npm run build --workspace=backend
FROM node:22-alpine AS runner
ENV NODE_ENV=production
WORKDIR /app
# Workspace manifests again (so npm can install prod deps for backend + shared)
COPY package.json package-lock.json ./
COPY backend/package.json backend/
COPY frontend/package.json frontend/
COPY shared/package.json shared/
# Install prod deps only
RUN npm ci --omit=dev
# Copy runtime artifacts
COPY --from=builder /app/backend/dist backend/dist
COPY --from=builder /app/backend/database.scheme.sql backend/
COPY --from=builder /app/backend/database.scheme.sql backend/database.scheme.sql
# Copy the full shared dist output (esm/cjs/types)
COPY --from=builder /app/shared/dist shared/dist
COPY shared/package.json shared/package.json
EXPOSE 8004
WORKDIR /app/backend
+1 -1
View File
@@ -11,4 +11,4 @@
],
"@babel/preset-typescript"
]
}
}
+1 -1
View File
@@ -27,4 +27,4 @@
"typescript": "~6.0.2",
"tsx": "^4.23.0"
}
}
}
+124 -112
View File
@@ -1,48 +1,61 @@
import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise";
import mysql, {
type ResultSetHeader,
type RowDataPacket,
} from "mysql2/promise";
import dotenv from "dotenv";
import {returnErrorCode} from "../../../services/helperFuncs.js";
import {PRODUCT_ERROR_CODE} from "@stockhome/shared";
import { returnErrorCode } from "../../../services/helperFuncs.js";
import { PRODUCT_ERROR_CODE } from "@stockhome/shared";
dotenv.config();
const pool = mysql.createPool({
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
});
export const newProduct = async (
name: string,
description: string,
price: string,
amount: string,
storage_location: string,
expiry_date: string,
bottling_date: string,
): Promise<boolean | { success: boolean; code: string; data: null; message: string }> => {
const newPrice = price !== "" ? price : null;
name: string,
description: string,
price: string,
amount: string,
storage_location: string,
expiry_date: string,
bottling_date: string,
): Promise<
boolean | { success: boolean; code: string; data: null; message: string }
> => {
const newPrice = price !== "" ? price : null;
const [result] = await pool.query<ResultSetHeader>(
"INSERT INTO products (name, description, price, amount, storage_location, expiry_date, bottling_date) VALUES (?, ?, ?, ?, UUID_TO_BIN(?), ?, ?)",
[name, description, newPrice, amount, storage_location, expiry_date, bottling_date],
);
const [result] = await pool.query<ResultSetHeader>(
"INSERT INTO products (name, description, price, amount, storage_location, expiry_date, bottling_date) VALUES (?, ?, ?, ?, UUID_TO_BIN(?), ?, ?)",
[
name,
description,
newPrice,
amount,
storage_location,
expiry_date,
bottling_date,
],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SP001",
data: null,
message: "New Product successfully created.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SP001",
data: null,
message: "New Product successfully created.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_CREATED);
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_CREATED);
};
export const productDetails = async (uuid: string) => {
const [result] = await pool.query<RowDataPacket[]>(
`SELECT BIN_TO_UUID(p.uuid) AS uuid,
const [result] = await pool.query<RowDataPacket[]>(
`SELECT BIN_TO_UUID(p.uuid) AS uuid,
p.name,
p.description,
p.price,
@@ -55,23 +68,23 @@ export const productDetails = async (uuid: string) => {
FROM products p
JOIN storage_locations s ON p.storage_location = s.uuid
WHERE p.uuid = UUID_TO_BIN(?)`,
[uuid],
);
[uuid],
);
if (result.length > 0) {
return {
success: true,
code: "SP003",
data: result[0],
message: "Product details successfully loaded.",
};
}
if (result.length > 0) {
return {
success: true,
code: "SP003",
data: result[0],
message: "Product details successfully loaded.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND);
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND);
};
export const allProducts = async () => {
const [result] = await pool.query<RowDataPacket[]>(`
const [result] = await pool.query<RowDataPacket[]>(`
SELECT BIN_TO_UUID(p.uuid) AS uuid,
p.name,
p.description,
@@ -89,52 +102,52 @@ export const allProducts = async () => {
WHERE p.deleted = 0
`);
if (result.length > 0) {
return {
success: true,
code: "SP002",
data: result,
message: "All products successfully loaded.",
};
}
if (result.length > 0) {
return {
success: true,
code: "SP002",
data: result,
message: "All products successfully loaded.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND);
return returnErrorCode(PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND);
};
export const setAmount = async (itemUUID: string, amount: number) => {
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
SET amount = ?
WHERE uuid = UUID_TO_BIN(?)`,
[amount, itemUUID],
);
[amount, itemUUID],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SP004",
data: null,
message: "Amount successfully updated.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SP004",
data: null,
message: "Amount successfully updated.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_AMOUNT_NOT_UPDATED);
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_AMOUNT_NOT_UPDATED);
};
export const updateItem = async (
itemUUID: string,
newValues: {
name: string;
description: string;
price: string;
amount: string;
storage_location_uuid: string;
expiry_date: string;
bottling_date: string;
},
itemUUID: string,
newValues: {
name: string;
description: string;
price: string;
amount: string;
storage_location_uuid: string;
expiry_date: string;
bottling_date: string;
},
) => {
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
SET name = ?,
description = ?,
price = ?,
@@ -143,47 +156,46 @@ export const updateItem = async (
expiry_date = ?,
bottling_date = ?
WHERE uuid = UUID_TO_BIN(?);`,
[
newValues.name,
newValues.description,
newValues.price,
newValues.amount,
newValues.storage_location_uuid,
newValues.expiry_date,
newValues.bottling_date,
itemUUID,
],
);
[
newValues.name,
newValues.description,
newValues.price,
newValues.amount,
newValues.storage_location_uuid,
newValues.expiry_date,
newValues.bottling_date,
itemUUID,
],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SP005",
data: null,
message: "Item updated successfully.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SP005",
data: null,
message: "Item updated successfully.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_UPDATED);
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_UPDATED);
};
export const deleteProduct = async (uuid: string) => {
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
const [result] = await pool.query<ResultSetHeader>(
`UPDATE products
SET deleted = 1
WHERE uuid = UUID_TO_BIN(?);`,
[uuid],
);
[uuid],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SP006",
data: null,
message: "Product deleted successfully.",
};
;
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SP006",
data: null,
message: "Product deleted successfully.",
};
}
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED);
};
return returnErrorCode(PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED);
};
+64 -61
View File
@@ -1,87 +1,90 @@
import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise";
import mysql, {
type ResultSetHeader,
type RowDataPacket,
} from "mysql2/promise";
import dotenv from "dotenv";
import {STORAGE_ERROR_CODE} from "@stockhome/shared";
import {returnErrorCode} from "../../../services/helperFuncs.js";
import { STORAGE_ERROR_CODE } from "@stockhome/shared";
import { returnErrorCode } from "../../../services/helperFuncs.js";
dotenv.config();
const pool = mysql.createPool({
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
});
export const allStorages = async () => {
const [result] = await pool.query<RowDataPacket[]>(
"SELECT BIN_TO_UUID(uuid) AS uuid, name, description, created_at, updated_at FROM storage_locations;",
);
const [result] = await pool.query<RowDataPacket[]>(
"SELECT BIN_TO_UUID(uuid) AS uuid, name, description, created_at, updated_at FROM storage_locations;",
);
if (result.length > 0) {
return {
success: true,
code: "SS001",
data: result,
message: "Successfully fetched all storage locations.",
};
}
if (result.length > 0) {
return {
success: true,
code: "SS001",
data: result,
message: "Successfully fetched all storage locations.",
};
}
return returnErrorCode(STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND);
return returnErrorCode(STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND);
};
export const newStorage = async (name: string, description: string) => {
const [result] = await pool.query<ResultSetHeader>(
"INSERT INTO storage_locations (name, description) VALUES (?, ?)",
[name, description],
);
const [result] = await pool.query<ResultSetHeader>(
"INSERT INTO storage_locations (name, description) VALUES (?, ?)",
[name, description],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SS002",
data: null,
message: "Successfully created new storage location.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SS002",
data: null,
message: "Successfully created new storage location.",
};
}
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_CREATED);
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_CREATED);
};
export const updateStorage = async (
uuid: string,
values: { name: string; description: string },
uuid: string,
values: { name: string; description: string },
) => {
const [result] = await pool.query<ResultSetHeader>(
"UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);",
[values.name, values.description, uuid],
);
const [result] = await pool.query<ResultSetHeader>(
"UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);",
[values.name, values.description, uuid],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SS003",
data: null,
message: "Successfully updated storage location.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SS003",
data: null,
message: "Successfully updated storage location.",
};
}
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_UPDATED);
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_UPDATED);
};
export const deleteStorage = async (uuid: string) => {
const [result] = await pool.query<ResultSetHeader>(
"DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);",
[uuid],
);
const [result] = await pool.query<ResultSetHeader>(
"DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);",
[uuid],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SS004",
data: null,
message: "Successfully deleted storage location.",
};
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SS004",
data: null,
message: "Successfully deleted storage location.",
};
}
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_DELETED);
};
return returnErrorCode(STORAGE_ERROR_CODE.STORAGE_NOT_DELETED);
};
+92 -89
View File
@@ -1,132 +1,135 @@
import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise";
import mysql, {
type ResultSetHeader,
type RowDataPacket,
} from "mysql2/promise";
import dotenv from "dotenv";
import {GENERAL_ERROR_CODE, USER_ERROR_CODE} from "@stockhome/shared";
import {returnErrorCode} from "../../../services/helperFuncs.js";
import { GENERAL_ERROR_CODE, USER_ERROR_CODE } from "@stockhome/shared";
import { returnErrorCode } from "../../../services/helperFuncs.js";
import type { AuthTokenPayload } from "../../../services/tokenService";
dotenv.config();
const pool = mysql.createPool({
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
});
export const findUser = async (username: string, password: string) => {
const [result] = await pool.query<RowDataPacket[]>(
"SELECT BIN_TO_UUID(uuid) AS uuid, username, first_name, last_name, email, is_admin, is_active, last_login FROM users WHERE username = ? AND password = ?;",
[username, password],
);
const [result] = await pool.query<RowDataPacket[]>(
"SELECT BIN_TO_UUID(uuid) AS uuid, username, first_name, last_name, email, is_admin, is_active, last_login FROM users WHERE username = ? AND password = ?;",
[username, password],
);
const userRow = result[0];
const userRow = result[0];
if (!userRow) {
return returnErrorCode(USER_ERROR_CODE.WRONG_USERNAME_PASSWORD);
}
if (!userRow) {
return returnErrorCode(USER_ERROR_CODE.WRONG_USERNAME_PASSWORD);
}
// Cast DB row to AuthTokenPayload for token generation / typing
const user = userRow as unknown as AuthTokenPayload;
// Cast DB row to AuthTokenPayload for token generation / typing
const user = userRow as unknown as AuthTokenPayload;
if (!user.is_active) {
return returnErrorCode(USER_ERROR_CODE.USER_IS_DEACTIVATED);
}
if (!user.is_active) {
return returnErrorCode(USER_ERROR_CODE.USER_IS_DEACTIVATED);
}
return {
success: true,
code: "SU001",
data: user,
message: "Successfully found user.",
};
return {
success: true,
code: "SU001",
data: user,
message: "Successfully found user.",
};
};
export const loginUser = async (username: string) => {
const [result] = await pool.query<ResultSetHeader>(
"UPDATE users SET last_login = NOW() WHERE username = ?;",
[username],
);
const [result] = await pool.query<ResultSetHeader>(
"UPDATE users SET last_login = NOW() WHERE username = ?;",
[username],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SU002",
data: null,
message: "Successfully logged in user.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SU002",
data: null,
message: "Successfully logged in user.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
};
export const updateSettings = async (payload: {
"app-name": string;
currency: string;
"app-name": string;
currency: string;
}) => {
const appName = payload["app-name"];
const currency = payload.currency;
const appName = payload["app-name"];
const currency = payload.currency;
const [result] = await pool.query<ResultSetHeader>(
`UPDATE app_settings
const [result] = await pool.query<ResultSetHeader>(
`UPDATE app_settings
SET value = CASE name
WHEN "app-name" THEN ?
WHEN "currency" THEN ?
ELSE value
END
WHERE name IN ("app-name", "currency");`,
[appName, currency],
);
[appName, currency],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SU003",
data: null,
message: "Successfully updated settings.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
if (result.affectedRows > 0) {
return {
success: true,
code: "SU003",
data: null,
message: "Successfully updated settings.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
};
export const getSettings = async () => {
const [result] = await pool.query<RowDataPacket[]>(
`SELECT *
const [result] = await pool.query<RowDataPacket[]>(
`SELECT *
FROM app_settings;`,
);
);
if (result.length > 0) {
return {
success: true,
code: "SU004",
data: result,
message: "Successfully fetched settings.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
if (result.length > 0) {
return {
success: true,
code: "SU004",
data: result,
message: "Successfully fetched settings.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
};
export const changePassword = async (
username: string,
currentPasswordUser: string,
newPassword: string,
username: string,
currentPasswordUser: string,
newPassword: string,
) => {
const [result] = await pool.query<ResultSetHeader>(
`UPDATE users
const [result] = await pool.query<ResultSetHeader>(
`UPDATE users
SET password = ?
WHERE username = ?
AND password = ?;`,
[newPassword, username, currentPasswordUser],
);
[newPassword, username, currentPasswordUser],
);
if (result.affectedRows > 0) {
return {
success: true,
code: "SU005",
data: null,
message: "Successfully fetched settings.",
};
} else {
return returnErrorCode(GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR);
}
};
if (result.affectedRows > 0) {
return {
success: true,
code: "SU005",
data: null,
message: "Successfully fetched settings.",
};
} else {
return returnErrorCode(USER_ERROR_CODE.PASSWORD_CHANGE_FAILED);
}
};
+245 -245
View File
@@ -1,284 +1,284 @@
import express from "express";
import dotenv from "dotenv";
import {authenticate} from "../../services/tokenService";
import { authenticate } from "../../services/tokenService";
import {
allProducts,
deleteProduct,
newProduct,
productDetails,
setAmount,
updateItem,
allProducts,
deleteProduct,
newProduct,
productDetails,
setAmount,
updateItem,
} from "./database/products.database";
import {GENERAL_ERROR_CODE, PRODUCT_ERROR_CODE} from "@stockhome/shared";
import { GENERAL_ERROR_CODE, PRODUCT_ERROR_CODE } from "@stockhome/shared";
dotenv.config();
const router = express.Router();
router.post("/new-product", authenticate, async (req, res) => {
const {
name,
description,
price,
amount,
storage_location,
expiry_date,
bottling_date,
} = req.body;
const {
name,
description,
price,
amount,
storage_location,
expiry_date,
bottling_date,
} = req.body;
if (!name) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await newProduct(
name,
description,
price,
amount,
storage_location,
expiry_date,
bottling_date,
);
if (!result || typeof result !== "object") {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP001") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (!name) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await newProduct(
name,
description,
price,
amount,
storage_location,
expiry_date,
bottling_date,
);
if (!result || typeof result !== "object") {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP001") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.get("/all-products", authenticate, async (req, res) => {
const result = await allProducts();
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP002") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
const result = await allProducts();
if (!result) {
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP002") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === PRODUCT_ERROR_CODE.NO_PRODUCTS_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.get("/view", async (req, res) => {
const uuid = req.query.uuid;
const uuid = req.query.uuid;
if (typeof uuid !== "string") {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await productDetails(uuid);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP003") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (typeof uuid !== "string") {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await productDetails(uuid);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP003") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === PRODUCT_ERROR_CODE.PRODUCT_NOT_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.put("/mutate/set-amount", authenticate, async (req, res) => {
const amount = req.query.amount;
const itemUUID = req.query.item;
const amount = req.query.amount;
const itemUUID = req.query.item;
if (typeof itemUUID !== "string" || typeof amount !== "number") {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await setAmount(itemUUID, amount);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP004") {
return res.status(200).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (typeof itemUUID !== "string" || typeof amount !== "number") {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await setAmount(itemUUID, amount);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP004") {
return res.status(200).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/mutate/update-item", authenticate, async (req, res) => {
const itemUUID = req.query.item;
const newValues = req.body;
const itemUUID = req.query.item;
const newValues = req.body;
if (typeof itemUUID !== "string" || !newValues) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await updateItem(itemUUID, newValues);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP005") {
return res.status(200).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (typeof itemUUID !== "string" || !newValues) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await updateItem(itemUUID, newValues);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SP005") {
return res.status(200).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/delete-selection", authenticate, async (req, res) => {
const uuidArray = req.body;
const uuidArray = req.body;
if (!Array.isArray(uuidArray) || uuidArray.length === 0) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
for (const uuid of uuidArray) {
const response = await deleteProduct(uuid);
if (!response) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (response.code !== "SP006") {
return res.status(500).json({
success: false,
code: response.code || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[0],
data: null,
message: response.message || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[1],
});
}
}
return res.status(202).json({
success: true,
code: "SP006",
data: null,
message: "All selected products deleted successfully.",
if (!Array.isArray(uuidArray) || uuidArray.length === 0) {
return res.status(400).json({
success: false,
code: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: PRODUCT_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
for (const uuid of uuidArray) {
const response = await deleteProduct(uuid);
if (!response) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (response.code !== "SP006") {
return res.status(500).json({
success: false,
code: response.code || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[0],
data: null,
message: response.message || PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[1],
});
}
}
return res.status(202).json({
success: true,
code: "SP006",
data: null,
message: "All selected products deleted successfully.",
});
});
export default router;
export default router;
+160 -155
View File
@@ -1,184 +1,189 @@
import express from "express";
import dotenv from "dotenv";
import {authenticate} from "../../services/tokenService";
import {allStorages, deleteStorage, newStorage, updateStorage,} from "./database/storage.database";
import {GENERAL_ERROR_CODE, STORAGE_ERROR_CODE} from "@stockhome/shared";
import { authenticate } from "../../services/tokenService";
import {
allStorages,
deleteStorage,
newStorage,
updateStorage,
} from "./database/storage.database";
import { GENERAL_ERROR_CODE, STORAGE_ERROR_CODE } from "@stockhome/shared";
dotenv.config();
const router = express.Router();
router.get("/all-storages", authenticate, async (req, res) => {
const result = await allStorages();
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR,
data: null,
message: "Unknown error",
});
}
if (result.code === "SS001") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
const result = await allStorages();
if (!result) {
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR,
data: null,
message: "Unknown error",
});
}
if (result.code === "SS001") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
if (result.code === STORAGE_ERROR_CODE.NO_STORAGE_LOCATIONS_FOUND[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/new-storage", authenticate, async (req, res) => {
const {name, description} = req.body;
const { name, description } = req.body;
let desc = description;
let desc = description;
if (!name) {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
if (description == "") {
desc = null;
}
const result = await newStorage(name, desc);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
});
}
if (result.code === "SS002") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (!name) {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
if (description == "") {
desc = null;
}
const result = await newStorage(name, desc);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
});
}
if (result.code === "SS002") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/update-storage", authenticate, async (req, res) => {
const storageUUID = req.query.storageUUID;
const values = req.body;
const storageUUID = req.query.storageUUID;
const values = req.body;
if (!storageUUID || !values) {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
if (typeof storageUUID !== "string") {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await updateStorage(storageUUID, values);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SS003") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (!storageUUID || !values) {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
if (typeof storageUUID !== "string") {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await updateStorage(storageUUID, values);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SS003") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.delete("/delete", authenticate, async (req, res) => {
const uuid = req.query.uuid;
const uuid = req.query.uuid;
if (typeof uuid !== "string") {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await deleteStorage(uuid);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SS004") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (typeof uuid !== "string") {
return res.status(400).json({
success: false,
code: STORAGE_ERROR_CODE.INVALID_PARAMETERS[0],
data: null,
message: STORAGE_ERROR_CODE.INVALID_PARAMETERS[1],
});
}
const result = await deleteStorage(uuid);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SS004") {
return res.status(201).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
export default router;
+220 -214
View File
@@ -1,245 +1,251 @@
import express from "express";
import dotenv from "dotenv";
import {authenticate, generateToken} from "../../services/tokenService";
import {changePassword, findUser, getSettings, loginUser, updateSettings,} from "./database/users.database";
import {GENERAL_ERROR_CODE, USER_ERROR_CODE} from "@stockhome/shared";
import { authenticate, generateToken } from "../../services/tokenService";
import {
changePassword,
findUser,
getSettings,
loginUser,
updateSettings,
} from "./database/users.database";
import { GENERAL_ERROR_CODE, USER_ERROR_CODE } from "@stockhome/shared";
dotenv.config();
const router = express.Router();
router.post("/verify-token", authenticate, async (req, res) => {
res.sendStatus(200);
res.sendStatus(200);
});
router.post("/update-app-settings", authenticate, async (req, res) => {
const values = req.body;
const values = req.body;
if (!values || Object.keys(values).length === 0) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await updateSettings(values);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU003") {
return res.status(201).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (!values || Object.keys(values).length === 0) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await updateSettings(values);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU003") {
return res.status(201).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.get("/settings", authenticate, async (req, res) => {
const result = await getSettings();
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU004") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
const result = await getSettings();
if (!result) {
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU004") {
return res.status(200).json({
success: true,
code: result.code,
data: result.data,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/login", async (req, res) => {
const username = req.body.username;
const password = req.body.password;
const username = req.body.username;
const password = req.body.password;
if (!username || !password) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await findUser(username, password);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === USER_ERROR_CODE.WRONG_USERNAME_PASSWORD[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
if (result.code === USER_ERROR_CODE.USER_IS_DEACTIVATED[0]) {
return res.status(403).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
if (result.code === "SU001") {
if (!result.data) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const token = await generateToken(result.data);
if (!token) {
return res.status(500).json({
success: false,
code: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[0],
data: null,
message: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[1],
});
}
if (!result.data) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const login = await loginUser(result.data.username);
if (!login) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (login.code !== "SU002") {
return res.status(500).json({
success: false,
code: login.code || USER_ERROR_CODE.LOGIN_FAILED[0],
data: null,
message: login.message || USER_ERROR_CODE.LOGIN_FAILED[1],
});
}
return res.status(202).json({
success: true,
code: "SU001",
data: {
token,
},
message: "User token generated successfully",
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
if (!username || !password) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await findUser(username, password);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === USER_ERROR_CODE.WRONG_USERNAME_PASSWORD[0]) {
return res.status(404).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
if (result.code === USER_ERROR_CODE.USER_IS_DEACTIVATED[0]) {
return res.status(403).json({
success: false,
code: result.code,
data: null,
message: result.message,
});
}
if (result.code === "SU001") {
if (!result.data) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const token = await generateToken(result.data);
if (!token) {
return res.status(500).json({
success: false,
code: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[0],
data: null,
message: USER_ERROR_CODE.TOKEN_GENERATION_FAILED[1],
});
}
if (!result.data) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const login = await loginUser(result.data.username);
if (!login) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (login.code !== "SU002") {
return res.status(500).json({
success: false,
code: login.code || USER_ERROR_CODE.LOGIN_FAILED[0],
data: null,
message: login.message || USER_ERROR_CODE.LOGIN_FAILED[1],
});
}
return res.status(202).json({
success: true,
code: "SU001",
data: {
token,
},
message: "User token generated successfully",
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
router.post("/change-password", authenticate, async (req, res) => {
const currentPassword = req.body.currentPassword;
const newPassword = req.body.newPassword;
if (!req.user) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const username = req.user.username;
if (!currentPassword || !newPassword) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await changePassword(username, currentPassword, newPassword);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU005") {
return res.status(202).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
const currentPassword = req.body.currentPassword;
const newPassword = req.body.newPassword;
if (!req.user) {
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
const username = req.user.username;
if (!currentPassword || !newPassword) {
return res.status(400).json({
success: false,
code: USER_ERROR_CODE.INVALID_REQUEST_BODY[0],
data: null,
message: USER_ERROR_CODE.INVALID_REQUEST_BODY[1],
});
}
const result = await changePassword(username, currentPassword, newPassword);
if (!result) {
return res.status(500).json({
success: false,
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
}
if (result.code === "SU005") {
return res.status(202).json({
success: true,
code: result.code,
data: null,
message: result.message,
});
}
return res.status(500).json({
success: false,
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
data: null,
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
});
});
export default router;
export default router;
+90 -87
View File
@@ -1,10 +1,9 @@
import type {NextFunction, Request, Response} from "express";
import type { NextFunction, Request, Response } from "express";
import express from "express";
import cors from "cors";
import dotenv from "dotenv";
import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2";
import {readFile} from "fs/promises";
// frontend routes
import mysql, { type ResultSetHeader, type RowDataPacket } from "mysql2";
import { readFile } from "fs/promises"; // frontend routes
import userRouter from "./routes/app/users.route";
import productRouter from "./routes/app/products.route";
import storageRouter from "./routes/app/storage.route";
@@ -18,16 +17,16 @@ const port = 8004;
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({extended: true}));
app.use(express.urlencoded({ extended: true }));
const pool = mysql
.createPool({
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
})
.promise();
.createPool({
host: process.env.DB_HOST!,
user: process.env.DB_USER!,
password: process.env.DB_PASSWORD!,
database: process.env.DB_NAME!,
})
.promise();
app.use("/users", userRouter);
@@ -36,113 +35,117 @@ app.use("/products", productRouter);
app.use("/storage", storageRouter);
app.listen(port, () => {
runStartup(port);
runStartup(port);
});
// Startup code
const runStartup = async (port: number) => {
let firstStartupValue: string | null = null;
try {
const [firstResponse] = await pool.query<RowDataPacket[]>(
`SELECT value
let firstStartupValue: string | null = null;
try {
const [firstResponse] = await pool.query<RowDataPacket[]>(
`SELECT value
FROM app_settings
WHERE name = "first-startup";`,
);
firstStartupValue = firstResponse[0]?.value ?? null;
} catch (err: unknown) {
if (err instanceof Error && (err as NodeJS.ErrnoException & { code?: string }).code !== "ER_NO_SUCH_TABLE") {
throw err;
}
);
firstStartupValue = firstResponse[0]?.value ?? null;
} catch (err: unknown) {
if (
err instanceof Error &&
(err as NodeJS.ErrnoException & { code?: string }).code !==
"ER_NO_SUCH_TABLE"
) {
throw err;
}
}
if (firstStartupValue !== "false") {
const schemaPath = path.join(__dirname, "database.scheme.sql");
const schemaSql = await readFile(schemaPath, "utf8");
const statements = schemaSql
.split(";")
.map((statement: string) => statement.trim())
.filter((statement: string) => statement.length > 0);
for (const statement of statements) {
await pool.query(statement);
}
if (firstStartupValue !== "false") {
const schemaPath = path.join(__dirname, "database.scheme.sql");
const schemaSql = await readFile(schemaPath, "utf8");
const statements = schemaSql
.split(";")
.map((statement: string) => statement.trim())
.filter((statement: string) => statement.length > 0);
for (const statement of statements) {
await pool.query(statement);
}
const [result] = await pool.query<RowDataPacket[]>(
`SELECT value
const [result] = await pool.query<RowDataPacket[]>(
`SELECT value
FROM app_settings
WHERE name = "first-startup";`,
);
);
if (result[0]?.value === "true") {
const insertResult = await insertFirstData();
if (result[0]?.value === "true") {
const insertResult = await insertFirstData();
if (insertResult.affectedRows > 0) {
console.log("Successfully created admin user!");
console.log("Username: admin");
console.log("Password: admin");
if (insertResult.affectedRows > 0) {
console.log("Successfully created admin user!");
console.log("Username: admin");
console.log("Password: admin");
const [scndResponse] = await pool.query<ResultSetHeader>(
`UPDATE app_settings
const [scndResponse] = await pool.query<ResultSetHeader>(
`UPDATE app_settings
SET value = "false"
WHERE name = "first-startup";`,
);
);
if (scndResponse.affectedRows > 0) {
console.log("Database settet up successfully!");
} else {
console.error("There was an error while setting up the database!");
}
} else {
console.error("Error while creating admin user.");
}
if (scndResponse.affectedRows > 0) {
console.log("Database settet up successfully!");
} else {
console.error("There was an error while setting up the database!");
}
} else {
console.error("Error while creating admin user.");
}
}
}
console.log("Everything is settet up successfully!");
console.log(`Backend is running on http://localhost:${port}`);
console.log("Everything is settet up successfully!");
console.log(`Backend is running on http://localhost:${port}`);
};
const insertFirstData = async (): Promise<ResultSetHeader> => {
const [insertResult] = await pool.query<ResultSetHeader>(
`INSERT INTO users (username, first_name, last_name, email, password, is_admin)
const [insertResult] = await pool.query<ResultSetHeader>(
`INSERT INTO users (username, first_name, last_name, email, password, is_admin)
VALUES ("admin", "admin", "admin", "admin@example.com", "admin", 1)`,
);
);
await pool.query(
`INSERT INTO storage_locations (name, description)
await pool.query(
`INSERT INTO storage_locations (name, description)
VALUES (?, ?);`,
["Default Storage", "Initial storage location"],
);
["Default Storage", "Initial storage location"],
);
const [storageRows] = await pool.query<RowDataPacket[]>(
`SELECT uuid
const [storageRows] = await pool.query<RowDataPacket[]>(
`SELECT uuid
FROM storage_locations
WHERE name = ? LIMIT 1;`,
["Default Storage"],
);
["Default Storage"],
);
const storageUuid: string | null = storageRows[0]?.uuid ?? null;
if (storageUuid) {
await pool.query(
`INSERT INTO products (name, description, price, amount, storage_location, picture)
const storageUuid: string | null = storageRows[0]?.uuid ?? null;
if (storageUuid) {
await pool.query(
`INSERT INTO products (name, description, price, amount, storage_location, picture)
VALUES (?, ?, ?, ?, ?, ?);`,
[
"Welcome Product",
"Your first item in Stockhome",
"0.00",
1,
storageUuid,
null,
],
);
}
[
"Welcome Product",
"Your first item in Stockhome",
"0.00",
1,
storageUuid,
null,
],
);
}
console.log("Welcome product is ready...");
return insertResult;
console.log("Welcome product is ready...");
return insertResult;
};
// error handling code
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
console.error(err.stack);
res.status(500).send("Something broke!");
});
console.error(err.stack);
res.status(500).send("Something broke!");
});
+11 -11
View File
@@ -1,14 +1,14 @@
import type {ErrorCode} from "../misc/types.ts";
import type { ErrorCode } from "../misc/types.ts";
export const returnErrorCode = (errorKey: ErrorCode) => {
if (!Array.isArray(errorKey) || errorKey.length < 2) {
return false;
}
if (!Array.isArray(errorKey) || errorKey.length < 2) {
return false;
}
return {
success: false,
code: errorKey[0],
data: null,
message: errorKey[1]
}
};
return {
success: false,
code: errorKey[0],
data: null,
message: errorKey[1],
};
};
+23 -19
View File
@@ -1,36 +1,40 @@
import type {NextFunction, Request, Response} from "express";
import {type JWTPayload, jwtVerify, SignJWT} from "jose";
import type { NextFunction, Request, Response } from "express";
import { type JWTPayload, jwtVerify, SignJWT } from "jose";
import env from "dotenv";
env.config();
const secret = new TextEncoder().encode(process.env.SECRET_KEY);
export type AuthTokenPayload = JWTPayload & {
username: string;
username: string;
};
declare module "express-serve-static-core" {
interface Request {
user?: AuthTokenPayload;
}
interface Request {
user?: AuthTokenPayload;
}
}
export async function generateToken(payload: AuthTokenPayload) {
return await new SignJWT(payload)
.setProtectedHeader({alg: "HS256"})
.setIssuedAt()
.setExpirationTime("24h")
.sign(secret);
return await new SignJWT(payload)
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("24h")
.sign(secret);
}
export async function authenticate(req: Request, res: Response, next: NextFunction) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
export async function authenticate(
req: Request,
res: Response,
next: NextFunction,
) {
const authHeader = req.headers["authorization"];
const token = authHeader && authHeader.split(" ")[1];
if (token == null) return res.sendStatus(401);
if (token == null) return res.sendStatus(401);
const {payload} = await jwtVerify<AuthTokenPayload>(token, secret);
req.user = payload;
const { payload } = await jwtVerify<AuthTokenPayload>(token, secret);
req.user = payload;
next();
}
next();
}
+3 -7
View File
@@ -9,17 +9,13 @@
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"ignoreDeprecations": "6.0",
"types": [
"node"
],
"types": ["node"],
"sourceMap": true,
"declaration": true,
"strict": true,
"skipLibCheck": true
},
"include": [
"./**/*.ts"
],
"include": ["./**/*.ts"],
"exclude": [
"node_modules",
"dist",
@@ -33,4 +29,4 @@
"path": "../shared"
}
]
}
}
+22 -22
View File
@@ -1,29 +1,29 @@
import {defineConfig} from 'vite'
import path from 'path'
import {dirname} from "node:path"
import {fileURLToPath} from "node:url"
import { defineConfig } from "vite";
import path from "path";
import { dirname } from "node:path";
import { fileURLToPath } from "node:url";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
export default defineConfig({
root: 'src',
build: {
outDir: '../dist',
emptyOutDir: true,
sourcemap: true,
minify: 'esbuild',
assetsDir: 'assets',
rollupOptions: {
input: path.resolve(__dirname, 'src/index.html'),
output: {
assetFileNames: 'assets/[name]-[hash][extname]',
},
},
root: "src",
build: {
outDir: "../dist",
emptyOutDir: true,
sourcemap: true,
minify: "esbuild",
assetsDir: "assets",
rollupOptions: {
input: path.resolve(__dirname, "src/index.html"),
output: {
assetFileNames: "assets/[name]-[hash][extname]",
},
},
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
},
},
resolve: {
alias: {
"@": path.resolve(__dirname, "src"),
},
})
},
});