refactor: change backend to typescript and update types and variables accordingly
This commit is contained in:
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
import type {AuthTokenPayload} from "../services/tokenService.js";
|
||||
|
||||
declare global {
|
||||
namespace Express {
|
||||
interface Request {
|
||||
user?: AuthTokenPayload;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,8 @@
|
||||
export type ErrorCode = readonly [code: string, message: string];
|
||||
|
||||
export interface Response {
|
||||
success: boolean;
|
||||
code: string;
|
||||
data: any;
|
||||
message: string;
|
||||
}
|
||||
@@ -2,22 +2,27 @@
|
||||
"name": "backend",
|
||||
"version": "0.0.0-dev",
|
||||
"description": "",
|
||||
"main": "server.js",
|
||||
"main": "server.ts",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"start": "node server.js",
|
||||
"dev": "node --watch server.js"
|
||||
"start": "node server.ts",
|
||||
"dev": "node --watch server.ts"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "module",
|
||||
"dependencies": {
|
||||
"@stockhome/shared": "1.0.0",
|
||||
"cors": "^2.8.5",
|
||||
"dotenv": "^17.2.3",
|
||||
"ejs": "^3.1.10",
|
||||
"express": "^5.2.1",
|
||||
"jose": "^6.0.12",
|
||||
"mysql2": "^3.16.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.6",
|
||||
"@types/node": "^26.1.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,151 +1,189 @@
|
||||
import mysql from "mysql2";
|
||||
import mysql, {type ResultSetHeader, type RowDataPacket} from "mysql2/promise";
|
||||
import dotenv from "dotenv";
|
||||
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,
|
||||
})
|
||||
.promise();
|
||||
const pool = mysql.createPool({
|
||||
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,
|
||||
description,
|
||||
price,
|
||||
amount,
|
||||
storage_location,
|
||||
expiry_date,
|
||||
bottling_date,
|
||||
) => {
|
||||
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(
|
||||
"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 { code: "sp001" };
|
||||
}
|
||||
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) => {
|
||||
const [result] = await pool.query(
|
||||
`SELECT
|
||||
BIN_TO_UUID(p.uuid) AS uuid,
|
||||
p.name,
|
||||
p.description,
|
||||
p.price,
|
||||
p.amount,
|
||||
BIN_TO_UUID(s.uuid) AS storage_location_uuid,
|
||||
s.name AS storage_location_name,
|
||||
p.expiry_date,
|
||||
p.bottling_date,
|
||||
p.picture
|
||||
FROM products p
|
||||
JOIN storage_locations s ON p.storage_location = s.uuid
|
||||
WHERE p.uuid = UUID_TO_BIN(?)`,
|
||||
[uuid],
|
||||
);
|
||||
export const productDetails = async (uuid: string) => {
|
||||
const [result] = await pool.query<RowDataPacket[]>(
|
||||
`SELECT BIN_TO_UUID(p.uuid) AS uuid,
|
||||
p.name,
|
||||
p.description,
|
||||
p.price,
|
||||
p.amount,
|
||||
BIN_TO_UUID(s.uuid) AS storage_location_uuid,
|
||||
s.name AS storage_location_name,
|
||||
p.expiry_date,
|
||||
p.bottling_date,
|
||||
p.picture
|
||||
FROM products p
|
||||
JOIN storage_locations s ON p.storage_location = s.uuid
|
||||
WHERE p.uuid = UUID_TO_BIN(?)`,
|
||||
[uuid],
|
||||
);
|
||||
|
||||
if (result.length > 0) {
|
||||
return { code: "sp003", data: result[0] };
|
||||
}
|
||||
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(`
|
||||
SELECT
|
||||
BIN_TO_UUID(p.uuid) AS uuid,
|
||||
p.name,
|
||||
p.description,
|
||||
p.price,
|
||||
p.amount,
|
||||
BIN_TO_UUID(s.uuid) AS storage_location_uuid,
|
||||
s.name AS storage_location_name,
|
||||
p.expiry_date,
|
||||
p.bottling_date,
|
||||
p.picture,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM products p
|
||||
JOIN storage_locations s ON p.storage_location = s.uuid
|
||||
WHERE p.deleted = 0
|
||||
`);
|
||||
const [result] = await pool.query<RowDataPacket[]>(`
|
||||
SELECT BIN_TO_UUID(p.uuid) AS uuid,
|
||||
p.name,
|
||||
p.description,
|
||||
p.price,
|
||||
p.amount,
|
||||
BIN_TO_UUID(s.uuid) AS storage_location_uuid,
|
||||
s.name AS storage_location_name,
|
||||
p.expiry_date,
|
||||
p.bottling_date,
|
||||
p.picture,
|
||||
p.created_at,
|
||||
p.updated_at
|
||||
FROM products p
|
||||
JOIN storage_locations s ON p.storage_location = s.uuid
|
||||
WHERE p.deleted = 0
|
||||
`);
|
||||
|
||||
if (result.length > 0) {
|
||||
return { code: "sp002", data: result };
|
||||
}
|
||||
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, amount) => {
|
||||
const [result] = await pool.query(
|
||||
`
|
||||
UPDATE products SET amount = ? WHERE uuid = UUID_TO_BIN(?)
|
||||
`,
|
||||
[amount, itemUUID],
|
||||
);
|
||||
export const setAmount = async (itemUUID: string, amount: number) => {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
`UPDATE products
|
||||
SET amount = ?
|
||||
WHERE uuid = UUID_TO_BIN(?)`,
|
||||
[amount, itemUUID],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "sp004" };
|
||||
}
|
||||
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, newValues) => {
|
||||
const [result] = await pool.query(
|
||||
`
|
||||
UPDATE products SET name = ?, description = ?, price = ?, amount = ?, storage_location = UUID_TO_BIN(?), 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,
|
||||
],
|
||||
);
|
||||
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;
|
||||
},
|
||||
) => {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
`UPDATE products
|
||||
SET name = ?,
|
||||
description = ?,
|
||||
price = ?,
|
||||
amount = ?,
|
||||
storage_location = UUID_TO_BIN(?),
|
||||
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,
|
||||
],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "sp005" };
|
||||
}
|
||||
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) => {
|
||||
const [result] = await pool.query(
|
||||
`UPDATE products SET deleted = 1 WHERE uuid = UUID_TO_BIN(?);`,
|
||||
[uuid],
|
||||
);
|
||||
export const deleteProduct = async (uuid: string) => {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
`UPDATE products
|
||||
SET deleted = 1
|
||||
WHERE uuid = UUID_TO_BIN(?);`,
|
||||
[uuid],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "sp006" };
|
||||
}
|
||||
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);
|
||||
};
|
||||
@@ -1,65 +1,87 @@
|
||||
import mysql from "mysql2";
|
||||
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";
|
||||
|
||||
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,
|
||||
})
|
||||
.promise();
|
||||
const pool = mysql.createPool({
|
||||
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(
|
||||
"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 { code: "ss001", data: result };
|
||||
}
|
||||
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, description) => {
|
||||
const [result] = await pool.query(
|
||||
"INSERT INTO storage_locations (name, description) VALUES (?, ?)",
|
||||
[name, description],
|
||||
);
|
||||
export const newStorage = async (name: string, description: string) => {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
"INSERT INTO storage_locations (name, description) VALUES (?, ?)",
|
||||
[name, description],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "ss002" };
|
||||
}
|
||||
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, values) => {
|
||||
const [result] = await pool.query(
|
||||
"UPDATE storage_locations SET name = ?, description = ? WHERE uuid = UUID_TO_BIN(?);",
|
||||
[values.name, values.description, uuid],
|
||||
);
|
||||
export const updateStorage = async (
|
||||
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],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "ss003" };
|
||||
}
|
||||
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) => {
|
||||
const [result] = await pool.query(
|
||||
"DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);",
|
||||
[uuid],
|
||||
);
|
||||
export const deleteStorage = async (uuid: string) => {
|
||||
const [result] = await pool.query<ResultSetHeader>(
|
||||
"DELETE FROM storage_locations WHERE uuid = UUID_TO_BIN(?);",
|
||||
[uuid],
|
||||
);
|
||||
|
||||
if (result.affectedRows > 0) {
|
||||
return { code: "ss004" };
|
||||
}
|
||||
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);
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import {authenticate} from "../../services/tokenService.js";
|
||||
import {authenticate} from "../../services/tokenService.ts";
|
||||
import {
|
||||
allProducts,
|
||||
deleteProduct,
|
||||
@@ -9,182 +9,276 @@ import {
|
||||
setAmount,
|
||||
updateItem,
|
||||
} from "./database/products.database.ts";
|
||||
import {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;
|
||||
|
||||
const result = await newProduct(
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
amount,
|
||||
storage_location,
|
||||
expiry_date,
|
||||
bottling_date,
|
||||
);
|
||||
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],
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "sp001") {
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
code: "sp001",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
const result = await newProduct(
|
||||
name,
|
||||
description,
|
||||
price,
|
||||
amount,
|
||||
storage_location,
|
||||
expiry_date,
|
||||
bottling_date,
|
||||
);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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();
|
||||
const result = await allProducts();
|
||||
|
||||
if (result.code === "sp002") {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
code: "sp002",
|
||||
data: result.data,
|
||||
message: "",
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
||||
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 (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,
|
||||
data: null,
|
||||
message: result.message,
|
||||
});
|
||||
});
|
||||
|
||||
router.get("/view", async (req, res) => {
|
||||
const uuid = req.query.uuid;
|
||||
const uuid = req.query.uuid;
|
||||
|
||||
const result = await productDetails(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],
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "sp003") {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
code: "sp003",
|
||||
data: result.data,
|
||||
message: "",
|
||||
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 (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,
|
||||
data: null,
|
||||
message: result.message,
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
|
||||
const result = await setAmount(itemUUID, amount);
|
||||
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],
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "sp004") {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
code: "sp004",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
const result = await setAmount(itemUUID, amount);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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", async (req, res) => {
|
||||
const itemUUID = req.query.item;
|
||||
const newValues = req.body;
|
||||
router.post("/mutate/update-item", authenticate, async (req, res) => {
|
||||
const itemUUID = req.query.item;
|
||||
const newValues = req.body;
|
||||
|
||||
const result = await updateItem(itemUUID, newValues);
|
||||
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],
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "sp005") {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
code: "sp005",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
const result = await updateItem(itemUUID, newValues);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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) => {
|
||||
let isError = false;
|
||||
const uuidArray = req.body;
|
||||
const uuidArray = req.body;
|
||||
|
||||
for (const uuid of uuidArray) {
|
||||
const response = await deleteProduct(uuid);
|
||||
if (!response || response.code !== "sp006") {
|
||||
isError = true;
|
||||
break;
|
||||
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],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isError === false) {
|
||||
return res.status(202).json({
|
||||
success: true,
|
||||
code: "sp006",
|
||||
data: null,
|
||||
message: "",
|
||||
success: true,
|
||||
code: "SP006",
|
||||
data: null,
|
||||
message: "All selected products deleted successfully.",
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[0],
|
||||
data: null,
|
||||
message: PRODUCT_ERROR_CODE.PRODUCT_NOT_DELETED[1],
|
||||
});
|
||||
});
|
||||
|
||||
export default router;
|
||||
export default router;
|
||||
@@ -2,119 +2,182 @@ import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import {authenticate} from "../../services/tokenService.js";
|
||||
import {allStorages, deleteStorage, newStorage, updateStorage,} from "./database/storage.database.ts";
|
||||
import {STORAGE_ERROR_CODE} from "@stockhome/shared";
|
||||
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();
|
||||
const result = await allStorages();
|
||||
|
||||
if (result.code === "ss001") {
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
code: "ss001",
|
||||
data: result.data,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
if (!result) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR,
|
||||
data: null,
|
||||
message: "Unknown error",
|
||||
});
|
||||
}
|
||||
|
||||
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,
|
||||
});
|
||||
}
|
||||
if (result.code === "SS001") {
|
||||
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,
|
||||
data: null,
|
||||
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: "invalid request body",
|
||||
});
|
||||
}
|
||||
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;
|
||||
}
|
||||
if (description == "") {
|
||||
desc = null;
|
||||
}
|
||||
|
||||
const result = await newStorage(name, desc);
|
||||
const result = await newStorage(name, desc);
|
||||
|
||||
if (result.code === "ss002") {
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
code: "ss002",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
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],
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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;
|
||||
|
||||
const result = await updateStorage(storageUUID, values);
|
||||
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 (result.code === "ss003") {
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
code: "ss003",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
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],
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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;
|
||||
|
||||
const result = await deleteStorage(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],
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "ss004") {
|
||||
return res.status(201).json({
|
||||
success: true,
|
||||
code: "ss004",
|
||||
data: null,
|
||||
message: "",
|
||||
});
|
||||
}
|
||||
const result = await deleteStorage(uuid);
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
data: null,
|
||||
message: result.message,
|
||||
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],
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import express from "express";
|
||||
import dotenv from "dotenv";
|
||||
import {authenticate, generateToken} from "../../services/tokenService.js";
|
||||
import {changePassword, findUser, getSettings, loginUser, updateSettings,} from "./database/users.database.ts";
|
||||
import {GENERAL_ERROR_CODE, USER_ERROR_CODE} from "@stockhome/shared";
|
||||
|
||||
dotenv.config();
|
||||
const router = express.Router();
|
||||
@@ -11,42 +12,71 @@ router.post("/verify-token", authenticate, async (req, res) => {
|
||||
});
|
||||
|
||||
router.post("/update-app-settings", authenticate, async (req, res) => {
|
||||
const result = await updateSettings(req.body);
|
||||
const values = req.body;
|
||||
|
||||
if (result.code === "su003") {
|
||||
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: "su003",
|
||||
code: result.code,
|
||||
data: result.data,
|
||||
message: null,
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
|
||||
data: null,
|
||||
message: result.message,
|
||||
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
|
||||
});
|
||||
});
|
||||
|
||||
router.get("/settings", authenticate, async (req, res) => {
|
||||
const result = await getSettings();
|
||||
|
||||
if (result.code === "su004") {
|
||||
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: "su004",
|
||||
code: result.code,
|
||||
data: result.data,
|
||||
message: null,
|
||||
message: result.message,
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
|
||||
data: null,
|
||||
message: result.message,
|
||||
message: result.message || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[1],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,9 +84,27 @@ router.post("/login", async (req, res) => {
|
||||
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.code === "EU001") {
|
||||
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,
|
||||
@@ -65,7 +113,7 @@ router.post("/login", async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "EU002") {
|
||||
if (result.code === USER_ERROR_CODE.USER_IS_DEACTIVATED[0]) {
|
||||
return res.status(403).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
@@ -74,22 +122,59 @@ router.post("/login", async (req, res) => {
|
||||
});
|
||||
}
|
||||
|
||||
if (result.code === "su001") {
|
||||
const token = await generateToken(result.data);
|
||||
const login = await loginUser(result.data.username);
|
||||
|
||||
if (login.code !== "su002") {
|
||||
if (result.code === "SU001") {
|
||||
if (!result.data) {
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: login.code,
|
||||
code: GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
|
||||
data: null,
|
||||
message: login.message,
|
||||
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",
|
||||
code: "SU001",
|
||||
data: {
|
||||
token,
|
||||
},
|
||||
@@ -99,31 +184,62 @@ router.post("/login", async (req, res) => {
|
||||
|
||||
return res.status(500).json({
|
||||
success: false,
|
||||
code: result.code,
|
||||
code: result.code || GENERAL_ERROR_CODE.UNEXPECTED_SERVER_ERROR[0],
|
||||
data: null,
|
||||
message: result.message,
|
||||
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.code === "su005") {
|
||||
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,
|
||||
message: result.message,
|
||||
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;
|
||||
@@ -1,138 +0,0 @@
|
||||
import express from "express";
|
||||
import cors from "cors";
|
||||
import dotenv from "dotenv";
|
||||
import mysql from "mysql2";
|
||||
import { readFile } from "fs/promises";
|
||||
dotenv.config();
|
||||
const app = express();
|
||||
app.set("view engine", "ejs");
|
||||
|
||||
const port = 8004;
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
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();
|
||||
|
||||
// frontend routes
|
||||
import userRouter from "./routes/app/users.route.js";
|
||||
app.use("/users", userRouter);
|
||||
|
||||
import productRouter from "./routes/app/products.route.js";
|
||||
app.use("/products", productRouter);
|
||||
|
||||
import storageRouter from "./routes/app/storage.route.js";
|
||||
app.use("/storage", storageRouter);
|
||||
|
||||
app.listen(port, () => {
|
||||
runStartup(port);
|
||||
});
|
||||
|
||||
// Startup code
|
||||
const runStartup = async (port) => {
|
||||
// Check if database is configured; create schema if app_settings is missing.
|
||||
let firstStartupValue = null;
|
||||
try {
|
||||
const [firstResponse] = await pool.query(
|
||||
`SELECT value FROM app_settings WHERE name = "first-startup";`,
|
||||
);
|
||||
firstStartupValue = firstResponse[0]?.value ?? null;
|
||||
} catch (err) {
|
||||
if (err?.code !== "ER_NO_SUCH_TABLE") {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstStartupValue !== "false") {
|
||||
const schemaPath = new URL("./database.scheme.sql", import.meta.url);
|
||||
const schemaSql = await readFile(schemaPath, "utf8");
|
||||
const statements = schemaSql
|
||||
.split(";")
|
||||
.map((statement) => statement.trim())
|
||||
.filter((statement) => statement.length > 0);
|
||||
|
||||
for (const statement of statements) {
|
||||
await pool.query(statement);
|
||||
}
|
||||
|
||||
// create admin credentials
|
||||
const [result] = await pool.query(
|
||||
`SELECT value FROM app_settings WHERE name = "first-startup";`,
|
||||
);
|
||||
|
||||
if (result[0]?.value === "true") {
|
||||
const insertResult = await insertFirstData();
|
||||
|
||||
if (insertResult.affectedRows > 0) {
|
||||
// print out admin credentials
|
||||
console.log("Successfully created admin user!");
|
||||
console.log("Username: admin");
|
||||
console.log("Password: admin");
|
||||
|
||||
// Set startup variable to true if scheme insert was successfull
|
||||
const [scndResponse] = await pool.query(
|
||||
`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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Everything is settet up successfully!");
|
||||
console.log(`Backend is running on http://localhost:${port}`);
|
||||
};
|
||||
|
||||
const insertFirstData = async () => {
|
||||
const [insertResult] = await pool.query(
|
||||
`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) VALUES (?, ?);`,
|
||||
["Default Storage", "Initial storage location"],
|
||||
);
|
||||
|
||||
const [storageRows] = await pool.query(
|
||||
`SELECT uuid FROM storage_locations WHERE name = ? LIMIT 1;`,
|
||||
["Default Storage"],
|
||||
);
|
||||
|
||||
const storageUuid = 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,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Welcome product is ready...")
|
||||
return insertResult;
|
||||
};
|
||||
|
||||
// error handling code
|
||||
app.use((err, req, res, next) => {
|
||||
console.error(err.stack);
|
||||
res.status(500).send("Something broke!");
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
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 userRouter from "./routes/app/users.route.ts";
|
||||
import productRouter from "./routes/app/products.route.ts";
|
||||
import storageRouter from "./routes/app/storage.route.ts";
|
||||
|
||||
dotenv.config();
|
||||
const app = express();
|
||||
app.set("view engine", "ejs");
|
||||
|
||||
const port = 8004;
|
||||
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
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();
|
||||
|
||||
app.use("/users", userRouter);
|
||||
|
||||
app.use("/products", productRouter);
|
||||
|
||||
app.use("/storage", storageRouter);
|
||||
|
||||
app.listen(port, () => {
|
||||
runStartup(port);
|
||||
});
|
||||
|
||||
// Startup code
|
||||
const runStartup = async (port: number) => {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstStartupValue !== "false") {
|
||||
const schemaPath = new URL("./database.scheme.sql", import.meta.url);
|
||||
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
|
||||
FROM app_settings
|
||||
WHERE name = "first-startup";`,
|
||||
);
|
||||
|
||||
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");
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
VALUES ("admin", "admin", "admin", "admin@example.com", "admin", 1)`,
|
||||
);
|
||||
|
||||
await pool.query(
|
||||
`INSERT INTO storage_locations (name, description)
|
||||
VALUES (?, ?);`,
|
||||
["Default Storage", "Initial storage location"],
|
||||
);
|
||||
|
||||
const [storageRows] = await pool.query<RowDataPacket[]>(
|
||||
`SELECT uuid
|
||||
FROM storage_locations
|
||||
WHERE name = ? LIMIT 1;`,
|
||||
["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)
|
||||
VALUES (?, ?, ?, ?, ?, ?);`,
|
||||
[
|
||||
"Welcome Product",
|
||||
"Your first item in Stockhome",
|
||||
"0.00",
|
||||
1,
|
||||
storageUuid,
|
||||
null,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
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!");
|
||||
});
|
||||
@@ -1,9 +1,14 @@
|
||||
export const returnErrorCode = (errorKey) => {
|
||||
import type {ErrorCode} from "../misc/types.ts";
|
||||
|
||||
export const returnErrorCode = (errorKey: ErrorCode) => {
|
||||
if (!Array.isArray(errorKey) || errorKey.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return {code: errorKey[0], message: errorKey[1], data: null};
|
||||
};
|
||||
|
||||
export default returnErrorCode;
|
||||
return {
|
||||
success: false,
|
||||
code: errorKey[0],
|
||||
data: null,
|
||||
message: errorKey[1]
|
||||
}
|
||||
};
|
||||
@@ -1,25 +1,36 @@
|
||||
import {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 async function generateToken(payload) {
|
||||
return await new SignJWT(payload)
|
||||
.setProtectedHeader({alg: "HS256"})
|
||||
.setIssuedAt()
|
||||
.setExpirationTime("24h") // Token valid for 24 hours
|
||||
.sign(secret);
|
||||
export type AuthTokenPayload = JWTPayload & {
|
||||
username: string;
|
||||
};
|
||||
|
||||
declare module "express-serve-static-core" {
|
||||
interface Request {
|
||||
user?: AuthTokenPayload;
|
||||
}
|
||||
}
|
||||
|
||||
export async function authenticate(req, res, next) {
|
||||
const authHeader = req.headers["authorization"];
|
||||
const token = authHeader && authHeader.split(" ")[1]; // Bearer <token>
|
||||
|
||||
if (token == null) return res.sendStatus(401); // No token present
|
||||
|
||||
const { payload } = await jwtVerify(token, secret);
|
||||
req.user = payload;
|
||||
|
||||
next();
|
||||
export async function generateToken(payload: AuthTokenPayload) {
|
||||
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];
|
||||
|
||||
if (token == null) return res.sendStatus(401);
|
||||
|
||||
const {payload} = await jwtVerify<AuthTokenPayload>(token, secret);
|
||||
req.user = payload;
|
||||
|
||||
next();
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
// Visit https://aka.ms/tsconfig to read more about this file
|
||||
"compilerOptions": {
|
||||
// File Layout
|
||||
// "rootDir": "./src",
|
||||
"outDir": "./dist",
|
||||
// Environment Settings
|
||||
// See also https://aka.ms/tsconfig/module
|
||||
"module": "esnext",
|
||||
"target": "esnext",
|
||||
"esModuleInterop": true,
|
||||
"moduleResolution": "bundler",
|
||||
// For nodejs:
|
||||
// "lib": ["esnext"],
|
||||
"types": [
|
||||
"node"
|
||||
],
|
||||
// and npm install -D @types/node
|
||||
|
||||
// Other Outputs
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
// Stricter Typechecking Options
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
// Style Options
|
||||
// "noImplicitReturns": true,
|
||||
// "noImplicitOverride": true,
|
||||
// "noUnusedLocals": true,
|
||||
// "noUnusedParameters": true,
|
||||
// "noFallthroughCasesInSwitch": true,
|
||||
// "noPropertyAccessFromIndexSignature": true,
|
||||
|
||||
// Recommended Options
|
||||
"strict": true,
|
||||
"jsx": "react-jsx",
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"moduleDetection": "force",
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
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', // Use esbuild for minification (default)
|
||||
assetsDir: 'assets',
|
||||
rollupOptions: {
|
||||
input: path.resolve(__dirname, 'src/index.html'),
|
||||
output: {
|
||||
assetFileNames: 'assets/[name]-[hash][extname]' // Hashing for cache busting
|
||||
}
|
||||
}
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src'), // Optional alias for cleaner imports
|
||||
}
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': '"production"' // Inject environment variables
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user