a lot of bugfixes, completed abstract products and products

This commit is contained in:
2024-10-27 04:45:13 +03:00
parent a27ce5762c
commit 75f9fb6d7d
24 changed files with 462 additions and 284 deletions

View File

@@ -1,13 +1,53 @@
import AbstractProductService from '../services/abstractproduct.js';
import statuses from '../utils/status.js';
import log from '../utils/log.js';
const TAG = "/controllers/abstractproduct.js"
class AbstractProductController {
async create(req, res) {
const { groupId, barcode, name, net_weight, image_filename, category, unit } = req.body;
// console.log(groupId, barcode, name, net_weight, image_filename, category, unit)
await AbstractProductService.create(groupId, barcode, name, net_weight, image_filename, category, unit);
return res.status(200).send("Successfull");
try {
const { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body;
await AbstractProductService.create(groupId, localId, barcode, name, net_weight, image_filename, category, unit);
return res.status(200).send("Successfull");
} catch (e) {
switch (e.status) {
case statuses.duplicate:
return res.status(400).send(e.message);
default:
log.error(e.original)
return res.status(500).send(e.message)
}
}
}
async update(req, res) {
try {
let { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body;
if (barcode) await AbstractProductService.updateBarcode(groupId, localId, barcode);
if (name) await AbstractProductService.updateName(groupId, localId, name);
if (net_weight) await AbstractProductService.updateNetWeight(groupId, localId, net_weight);
if (image_filename) await AbstractProductService.updateImageFilename(groupId, localId, image_filename);
if (category) await AbstractProductService.updateCategory(groupId, localId, category);
if (unit) await AbstractProductService.updateUnit(groupId, localId, unit);
return res.status(200).send("Successfull");
} catch (e) {
switch (e.status) {
case statuses.invalid_syntax:
log.error(e.original);
return res.status(400).send(e.message);
default:
log.error(e.original);
return res.status(500).send(e.message);
}
}
}
};

View File

@@ -14,7 +14,7 @@ class GroupController {
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
let status = await GroupService.create(groupName, user.login.id);
log.info(`New group with name ${groupName} was just created by user ${user.login.username}`);
UserService.joinGroup(user.login.id, status.id);

View File

@@ -0,0 +1,48 @@
import ProductService from '../services/product.js';
import statuses from '../utils/status.js';
import log from '../utils/log.js';
const TAG = "/controllers/product.js"
class AbstractProductController {
async create(req, res) {
try {
const { groupId, localId, abstract_product_id, amount, date_of_production, expiry_date } = req.body;
await ProductService.create(groupId, localId, abstract_product_id, amount, date_of_production, expiry_date);
return res.status(200).send("Successfull");
} catch (e) {
switch (e.status) {
case statuses.duplicate:
return res.status(400).send(e.message);
default:
log.error(e.original)
return res.status(500).send(e.message)
}
}
}
async update(req, res) {
try {
let { groupId, localId, abstract_product_id, amount, date_of_production, expiry_date } = req.body;
if (abstract_product_id) await ProductService.updateAbstractProductId(groupId, localId, abstract_product_id);
if (amount) await ProductService.updateAmount(groupId, localId, amount)
if (date_of_production) await ProductService.updateDateOfProduction(groupId, localId, date_of_production);
if (expiry_date) await ProductService.updateExpiryDate(groupId, localId, expiry_date);
} catch (e) {
switch (e.status) {
case statuses.invalid_syntax:
log.error(e.original);
return res.status(400).send(e.message);
}
}
return res.status(200).send("Successfull");
}
};
export default new AbstractProductController();

View File

@@ -2,16 +2,18 @@ import UserService from '../services/user.js';
import log from '../utils/log.js';
import bcrypt from 'bcrypt';
import genToken from '../utils/jwt.js';
import AbstractProductService from '../services/abstractproduct.js';
import ProductService from '../services/product.js';
const TAG = "/controllers/userjs"
class UserController {
async register (req, res) {
async register(req, res) {
try {
const {username, password} = req.body;
const { username, password } = req.body;
await UserService.create(username, password);
log.info(`New user with name ${username} has just registered`);
return res.status(200).send("Successfull register")
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/register: ${e}`)); }
@@ -19,7 +21,7 @@ class UserController {
async login(req, res) {
try {
const {username, password} = req.body;
const { username, password } = req.body;
const user = await UserService.getByUsername(username);
if (!bcrypt.compareSync(password, user.password)) return res.status(401).send("Wrong password");
@@ -28,6 +30,19 @@ class UserController {
return res.status(200).send(token);
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/login: ${e}`)); }
}
async synchronize(req, res) {
try {
const { groupId } = req.params;
let result = {}
result.abstract_products = await AbstractProductService.getAll(groupId);
result.products = await ProductService.getAll(groupId);
// result.categories = await CategoryService.getAll(groupId);
return res.status(200).json(result);
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/synchronize: ${e}`)); }
}
}
export default new UserController()

View File

@@ -6,7 +6,7 @@ import config from '../config.json' with {type: "json"};
const { Pool } = pg;
log.debug(
`Connecting to PG database ${config.dbname}@${config.dbhost}:${config.dbport} with credentials:
`Connecting to PG database ${config.dbname}@${config.dbhost}:${config.dbport} with credentials:
username: ${config.dbuser}
password: <hidden>`
);
@@ -19,7 +19,7 @@ const pool = new Pool({
password: config.dbpassword
});
log.debug("Database connection successful. Creating tables");
log.debug("Database connection successfull. Creating tables");
pool.query(fs.readFileSync('./db_schema.psql').toString());

View File

@@ -2,19 +2,20 @@ import express from 'express';
import UserRouter from './routers/user.js';
import GroupRouter from './routers/group.js';
import AbstractProductRouter from './routers/abstractproduct.js';
// import AdminRouter from './routers/admin.js';
import log from './utils/log.js'
import config from '../config.json' with {type: "json"};
import ProductRouter from './routers/product.js';
const app = express();
app.use(express.urlencoded({extended: true}));
app.use(express.urlencoded({ extended: true }));
app.use(express.json());
app.use('/api/user/', UserRouter);
app.use('/api/group/', GroupRouter);
app.use('/api/abstractproduct', AbstractProductRouter);
// app.use('/api/admin/', AdminRouter);
app.use('/api/product', ProductRouter);
app.listen(config.port, () => {
log.info(`Application has started on port ${config.port}`)

View File

@@ -10,9 +10,9 @@ const requireUsername = async (req, res, next) => {
if (req.method == "OPTIONS") next();
try {
const {username} = req.body;
if (!username) return res.status(400).send("Username is required");
next();
const { username } = req.body;
if (!username) return res.status(400).send("Username is required");
next();
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requireUsername: ${e}`)); }
}
@@ -20,9 +20,9 @@ const requirePassword = async (req, res, next) => {
if (req.method == "OPTIONS") next();
try {
const {password} = req.body;
if (!password) return res.status(400).send("Password is required");
next();
const { password } = req.body;
if (!password) return res.status(400).send("Password is required");
next();
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requirePassword: ${e}`)); }
}
@@ -48,7 +48,7 @@ const authorizeGroupOwner = async (req, res, next) => {
const { groupId } = req.params;
let user = jwt.decode(token, config.secret)
let adminId = await GroupService.getAdminId(groupId);
if (user.login.id != adminId) return res.status(403).send("Not your group");
next();
@@ -67,7 +67,7 @@ const checkGroupPassword = async (req, res, next) => {
if (groupPassword != password) return res.status(403).send("Wrong password");
next();
} catch (e) {return res.status(500).send(log.unknownError(`${TAG}/checkGroupPassword: ${e}`));}
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/checkGroupPassword: ${e}`)); }
}
const userIsInGroup = async (req, res, next) => {

View File

@@ -1,5 +1,7 @@
import UserService from '../services/user.js';
import GroupService from '../services/group.js';
import AbstractProductService from '../services/abstractproduct.js';
import ProductService from '../services/product.js';
import log from '../utils/log.js';
import statuses from '../utils/status.js';
@@ -57,4 +59,34 @@ const groupNameDoesntExist = async (req, res, next) => {
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupNameDoesntExist: ${e}`)) }
}
export default { usernameExists, usernameDoesntExist, groupExists, groupDoesntExist, groupNameDoesntExist }
const abstractProductExists = async (req, res, next) => {
try {
const { groupId, localId } = req.body;
let result = await AbstractProductService.exists(groupId, localId);
if (!result) return res.status(404).send("Abstract product not found");
next();
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/abstractProductExists: ${e}`)) }
}
const productExists = async (req, res, next) => {
try {
const { groupId, localId } = req.body;
let result = await ProductService.exists(groupId, localId);
if (!result) return res.status(404).send("Product not found");
next();
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/productExists: ${e}`)) }
}
export default {
usernameExists,
usernameDoesntExist,
groupExists,
groupDoesntExist,
groupNameDoesntExist,
abstractProductExists,
productExists
}

View File

@@ -6,5 +6,6 @@ import existance from '../middlewares/existance.js';
const AbstractProductRouter = new Router();
AbstractProductRouter.post('/create', auth.authenticate, existance.groupExists, auth.userIsInGroup, AbstractProductController.create);
AbstractProductRouter.post('/update', auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.abstractProductExists, AbstractProductController.update)
export default AbstractProductRouter;

11
src/routers/product.js Normal file
View File

@@ -0,0 +1,11 @@
import { Router } from 'express';
import auth from '../middlewares/auth.js';
import ProductController from '../controllers/product.js'
import existance from '../middlewares/existance.js';
const ProductRouter = new Router();
ProductRouter.post('/create', auth.authenticate, existance.groupExists, auth.userIsInGroup, ProductController.create);
ProductRouter.post('/update', auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.productExists, ProductController.update)
export default ProductRouter;

View File

@@ -7,5 +7,6 @@ const UserRouter = new Router();
UserRouter.post('/register', auth.requireUsername, auth.requirePassword, existance.usernameDoesntExist, UserController.register);
UserRouter.post('/login', auth.requireUsername, auth.requirePassword, existance.usernameExists, UserController.login);
UserRouter.get('/synchronize/:groupId', auth.authenticate, existance.groupExists, auth.userIsInGroup, UserController.synchronize);
export default UserRouter;

View File

@@ -1,9 +1,67 @@
import db from '../db.js';
import statuses from '../utils/status.js';
import errorHandler from '../utils/pgerrorhandler.js';
class AbstractProductService {
async create (groupid, barcode, name, net_weight, image_filename, category, unit) {
await db.query("INSERT INTO abstract_products (group_id, barcode, name, net_weight, image_filename, category, unit) VALUES ($1, $2, $3, $4, $5, $6, $7)", [groupid, barcode, name, net_weight, image_filename, category, unit]);
async create(groupid, localid, barcode, name, net_weight, image_filename, category, unit) {
await db.query("INSERT INTO abstract_products (group_id, local_id, barcode, name, net_weight, image_filename, category, unit) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", [groupid, localid, barcode, name, net_weight, image_filename, category, unit])
.catch((e) => {
errorHandler(e, "abstract product")
});
}
async updateBarcode(groupId, localId, barcode) {
await db.query("UPDATE abstract_products SET barcode = $1 WHERE group_id = $2 AND local_id = $3", [barcode, groupId, localId])
.catch((e) => {
errorHandler(e, "barcode")
});
}
async updateName(groupId, localId, name) {
await db.query("UPDATE abstract_products SET name = $1 WHERE group_id = $2 AND local_id = $3", [name, groupId, localId])
.catch((e) => {
errorHandler(e, "name")
});
}
async updateNetWeight(groupId, localId, net_weight) {
await db.query("UPDATE abstract_products SET net_weight = $1 WHERE group_id = $2 AND local_id = $3", [net_weight, groupId, localId]
.catch((e) => {
errorHandler(e, "net weight")
}));
}
async updateImageFilename(groupId, localId, image_filename) {
await db.query("UPDATE abstract_products SET image_filename = $1 WHERE group_id = $2 AND local_id = $3", [image_filename, groupId, localId])
.catch((e) => {
errorHandler(e, "image filename")
});
}
async updateCategory(groupId, localId, category) {
await db.query("UPDATE abstract_products SET category = $1 WHERE group_id = $2 AND local_id = $3", [category, groupId, localId])
.catch((e) => {
errorHandler(e, "category")
});
}
async updateUnit(groupId, localId, unit) {
await db.query("UPDATE abstract_products SET unit = $1 WHERE group_id = $2 AND local_id = $3", [unit, groupId, localId])
.catch((e) => {
errorHandler(e, "unit")
});
}
async getAll(groupId) {
let result = (await db.query("SELECT local_id, barcode, name, net_weight, image_filename, category, unit FROM abstract_products WHERE group_id = $1", [groupId])).rows
if (!result) return statuses.not_found;
return result
}
async exists(groupId, localId) {
let result = (await db.query("SELECT * FROM abstract_products WHERE group_id = $1 AND local_id = $2", [groupId, localId])).rowCount;
if (!result) return false;
return true;
}
};

View File

@@ -1,13 +1,10 @@
import db from '../db.js';
import errorHandler from '../utils/pgerrorhandler.js';
import status from '../utils/status.js';
class GroupService {
async create(name, creatorId) {
let res = await db.query("INSERT INTO groups (name, admin_id) VALUES ($1, $2) RETURNING ID", [name, creatorId]).catch (e => {
if (e.code == 23505) { // already exists
return status.duplicate;
}
})
let res = await db.query("INSERT INTO groups (name, admin_id) VALUES ($1, $2) RETURNING ID", [name, creatorId]).catch(errorHandler)
return res.rows[0];
}

55
src/services/product.js Normal file
View File

@@ -0,0 +1,55 @@
import db from '../db.js';
import statuses from '../utils/status.js';
import errorHandler from '../utils/pgerrorhandler.js';
class ProductService {
async create(groupid, localid, abstract_product_id, amount, date_of_production, expiry_date) {
await db.query("INSERT INTO products (group_id, local_id, abstract_product_id, amount, date_of_production, expiry_date) VALUES ($1, $2, $3, $4, $5, $6)", [groupid, localid, abstract_product_id, amount, date_of_production, expiry_date])
.catch((e) => {
errorHandler(e, "Abstract Product")
})
}
async updateAbstractProductId(groupId, localId, abstract_product_id) {
await db.query("UPDATE products SET abstract_product_id = $1 WHERE group_id = $2 AND local_id = $3", [abstract_product_id, groupId, localId])
.catch((e) => {
errorHandler(e, "abstract product id")
});
}
async updateAmount(groupId, localId, amount) {
await db.query("UPDATE products SET amount = $1 WHERE group_id = $2 AND local_id = $3", [amount, groupId, localId])
.catch((e) => {
errorHandler(e, "amount")
});
}
async updateDateOfProduction(groupId, localId, date_of_production) {
await db.query("UPDATE products SET date_of_production = $1 WHERE group_id = $2 AND local_id = $3", [date_of_production, groupId, localId])
.catch((e) => {
errorHandler(e, "date of production")
})
}
async updateExpiryDate(groupId, localId, expiry_date) {
await db.query("UPDATE products SET expiry_date = $1 WHERE group_id = $2 AND local_id = $3", [expiry_date, groupId, localId])
.catch((e) => {
errorHandler(e, "expiry date")
})
}
async getAll(groupId) {
let result = (await db.query("SELECT local_id, abstract_product_id, amount, date_of_production, expiry_date FROM products WHERE group_id = $1", [groupId])).rows
if (!result) return statuses.not_found;
return result
}
async exists(groupId, localId) {
let result = (await db.query("SELECT * FROM products WHERE group_id = $1 AND local_id = $2", [groupId, localId])).rowCount;
if (!result) return false;
return true;
}
};
export default new ProductService();

View File

@@ -3,11 +3,9 @@ import statuses from '../utils/status.js';
import bcrypt from 'bcrypt';
class UserService {
async create (username, password) {
await db.query("INSERT INTO users (username, password) VALUES ($1, $2)", [username, bcrypt.hashSync(password, 12)]).catch (e => {
if (e.code == 23505) { // already exists
return statuses.duplicate
}
async create(username, password) {
await db.query("INSERT INTO users (username, password) VALUES ($1, $2)", [username, bcrypt.hashSync(password, 12)]).catch((e) => {
errorHandler(e, "user")
})
return statuses.ok
}

View File

@@ -1,11 +0,0 @@
import { createHash } from 'node:crypto'
const hashAbstractProduct = (barcode, name, net_weight, image_filename, category, unit) => {
return createHash('md5').update(`${barcode}${name}${net_weight}${image_filename}${category}${unit}`).digest('hex');
}
const hashProduct = (abstract_product_id, amount, date_of_production, expiry_date) => {
return createHash('md5').update(`${abstract_product_id}${amount}${date_of_production}${expiry_date}`).digest('hex');
}
export default { hashAbstractProduct, hashProduct };

View File

@@ -3,7 +3,7 @@ import config from '../../config.json' with {type: "json"};
const genToken = (login) => {
const payload = { login };
return jwt.sign(payload, config.secret, {expiresIn: "7d"});
return jwt.sign(payload, config.secret, { expiresIn: "7d" });
};
export default genToken;

View File

@@ -0,0 +1,29 @@
import statuses from "./status.js"
const errorHandler = (e, obj) => {
switch (e.code) {
case '23505':
throw {
status: statuses.duplicate,
message: `Such ${obj} already exists`
}
case '22007':
throw {
status: statuses.invalid_syntax,
message: `Invalid syntax in ${obj}`
}
case '22001':
throw {
status: statuses.invalid_syntax,
message: `Value too long (${obj})`
}
default:
throw {
status: statuses.unknown,
message: `Unknown error. Please, report to the developer`,
original: e
}
}
};
export default errorHandler;

View File

@@ -1,7 +1,9 @@
const statuses = {
ok: "ok",
duplicate: "duplicate",
not_found: "not found"
not_found: "not found",
invalid_syntax: "invalid syntax",
unknown: "unknown"
}
export default statuses