diff --git a/config.json b/config.json new file mode 100644 index 0000000..753c0a1 --- /dev/null +++ b/config.json @@ -0,0 +1,10 @@ +{ + "debug": true, + "port": "8081", + "dbuser": "bsfe", + "dbhost": "localhost", + "dbport": "5432", + "dbpassword": "Ch@NgEME!", + "dbname": "bsfe", + "secret": "SECRET!11!1!" +} diff --git a/messages/en-US/msgs.json b/messages/en-US/msgs.json index 7e6d4bf..a1a79ae 100644 --- a/messages/en-US/msgs.json +++ b/messages/en-US/msgs.json @@ -4,6 +4,7 @@ "user duplicate": "Such user already exists!", "user invalid syntax": "Invalid syntax in one of user's parameters!", "user already in group": "User is already in group!", + "user not found": "User does not exists!", "username taken": "Username is taken!", "username not found": "Such username not found!", "username required": "Username is required!", @@ -15,8 +16,6 @@ "group not an owner": "You are not an owner of this group!", "group not a member": "You are not a member of this group!", "abstract product not found": "Such abstract product is not found!", - "hash without file": "Please, supply a hash of an image!", - "file without hash": "Please, supply an image!", "abstract product invalid syntax": "Invalid syntax in one of user's parameters!", "barcode not found": "Such barcode not found!", "barcode duplicate": "Such barcode already exists!", diff --git a/messages/ru-RU/msgs.json b/messages/ru-RU/msgs.json index 109a142..28ba16f 100644 --- a/messages/ru-RU/msgs.json +++ b/messages/ru-RU/msgs.json @@ -4,6 +4,7 @@ "user duplicate": "Такой пользователь уже существует!", "user invalid syntax": "Неправильный синткасис в одном из параметров пользователя!", "user already in group": "Пользователь уже в группе!", + "user not found": "Пользователь не существует!", "username taken": "Имя пользователя занято!", "username not found": "Такое имя пользователя не найдено!", "username required": "Требуется имя пользователя!", @@ -15,8 +16,6 @@ "group not an owner": "Вы не владелец этой группы!", "group not a member": "Вы не участник этой группы!", "abstract product not found": "Такой абстрактный продукт не найден!", - "hash without file": "Пожалуйста, укажите хеш изображения!", - "file without hash": "Пожалуйста, укажите изображение!", "abstract product invalid syntax": "Неправильный синткасис в одном из параметров абстрактного продукта!", "barcode not found": "Такой штрихкод не найден!", "barcode duplicate": "Такой штрихкод уже существует!", diff --git a/src/controllers/abstractproduct.js b/src/controllers/abstractproduct.js index 1c292d8..12ddaf2 100644 --- a/src/controllers/abstractproduct.js +++ b/src/controllers/abstractproduct.js @@ -4,14 +4,17 @@ import path from 'path'; import customError from '../response/customError.js'; import responseCodes from '../response/responseCodes.js'; import translate from '../utils/translate.js'; +import { createHash } from 'crypto'; const TAG = "/controllers/abstractproduct.js"; class AbstractProductController { async create(req, res) { - const { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body; + const { groupId, localId, barcode, name, net_weight, category, unit } = req.body; const tempPath = req.file.path; + const image_buffer = fs.readFileSync(tempPath); + const image_filename = createHash('md5').update(image_buffer).digest('hex'); const targetPath = path.join(path.resolve(path.dirname('')), `/uploads/${image_filename}.png`); if (path.extname(req.file.originalname).toLowerCase() !== ".png") { @@ -27,11 +30,18 @@ class AbstractProductController { } async update(req, res) { - let { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body; + let { groupId, localId, barcode, name, net_weight, category, unit } = req.body; - const tempPath = req.file.path; - const targetPath = path.join(path.resolve(path.dirname('')) + `/uploads/${image_filename}.png`); + var tempPath, image_buffer, image_filename, targetPath; + if (req.file) { + tempPath = req.file.path; + image_buffer = fs.readFileSync(tempPath); + image_filename = createHash('md5').update(image_buffer).digest('hex'); + targetPath = path.join(path.resolve(path.dirname('')) + `/uploads/${image_filename}.png`); + fs.copyFileSync(tempPath, targetPath); + fs.rmSync(tempPath); + } if (barcode) await AbstractProductService.updateBarcode(groupId, localId, barcode); @@ -39,16 +49,7 @@ class AbstractProductController { if (net_weight) await AbstractProductService.updateNetWeight(groupId, localId, net_weight); - if (image_filename && tempPath) { - fs.copyFileSync(tempPath, targetPath); - fs.rmSync(tempPath); - await AbstractProductService.updateImageFilename(groupId, localId, image_filename); - } else if (image_filename && !tempPath) { - throw new customError(`Abstract product update image hash without file`, responseCodes.responses.abstractproducts.hash_without_file); - } else if (!image_file && tempPath) { - fs.rmSync(tempPath); - throw new customError(`Abstract product update file without image hash`, responseCodes.responses.abstractproducts.file_without_hash); - } + if (tempPath) await AbstractProductService.updateImageFilename(groupId, localId, image_filename); if (category) await AbstractProductService.updateCategory(groupId, localId, category); diff --git a/src/controllers/category.js b/src/controllers/category.js index 6f94bed..ac61239 100644 --- a/src/controllers/category.js +++ b/src/controllers/category.js @@ -1,5 +1,6 @@ import CategoryService from "../services/category.js"; import translate from "../utils/translate.js"; +import responseCodes from "../response/responseCodes.js"; const TAG = "controllers/category.js"; diff --git a/src/controllers/group.js b/src/controllers/group.js index 6d84f0d..4ec6568 100644 --- a/src/controllers/group.js +++ b/src/controllers/group.js @@ -4,6 +4,7 @@ import jwt from 'jsonwebtoken'; import config from '../../config.json' with {type: "json"}; import log from '../utils/log.js'; import translate from '../utils/translate.js'; +import responseCodes from '../response/responseCodes.js'; const TAG = "/controllers/group.js"; @@ -16,7 +17,7 @@ class GroupController { log.info(`New group with name ${groupName} was just created by user ${user.login.username}`); - UserService.joinGroup(user.login.id, status.id); + await UserService.joinGroup(user.login.id, status.id); return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok)); } async join(req, res) { diff --git a/src/controllers/product.js b/src/controllers/product.js index f14d851..2cbab92 100644 --- a/src/controllers/product.js +++ b/src/controllers/product.js @@ -1,5 +1,6 @@ import ProductService from '../services/product.js'; import translate from '../utils/translate.js'; +import responseCodes from '../response/responseCodes.js'; const TAG = "/controllers/product.js" diff --git a/src/controllers/user.js b/src/controllers/user.js index 7f37c17..7d38dfa 100644 --- a/src/controllers/user.js +++ b/src/controllers/user.js @@ -5,6 +5,7 @@ import genToken from '../utils/jwt.js'; import AbstractProductService from '../services/abstractproduct.js'; import ProductService from '../services/product.js'; import translate from '../utils/translate.js'; +import responseCodes from '../response/responseCodes.js'; const TAG = "/controllers/userjs" @@ -25,7 +26,7 @@ class UserController { if (!bcrypt.compareSync(password, user.password)) return res.status(401).send("Wrong password"); const token = genToken(user); - return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok)); + return res.status(200).send(token); } async synchronize(req, res) { @@ -36,7 +37,7 @@ class UserController { result.products = await ProductService.getAll(groupId); result.categories = await CategoryService.getAll(groupId); - return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok)); + return res.status(200).send(result); } } diff --git a/src/middlewares/auth.js b/src/middlewares/auth.js index ba71dd5..1ee7468 100644 --- a/src/middlewares/auth.js +++ b/src/middlewares/auth.js @@ -1,4 +1,3 @@ -import log from '../utils/log.js' import jwt from 'jsonwebtoken'; import config from '../../config.json' with {type: "json"}; import GroupService from '../services/group.js'; @@ -26,10 +25,15 @@ const requirePassword = async (req, res, next) => { const authenticate = async (req, res, next) => { if (req.method == "OPTIONS") next(); - if (!req.headers.authorization) throw new customError(`authenticate no authorization header`, responseCodes.responses.authentication.not_found); + const token = req.headers.authorization.split(' ')[1]; if (!token) throw new customError(`authenticate no authorization token in header`, responseCodes.responses.authentication.not_found); + + let user = jwt.decode(token, config.secret); + + await UserService.getByUsername(user.login.username) + if (!jwt.verify(token, config.secret)) throw new customError(`authenticate token is invalid`, responseCodes.responses.authentication.invalid); next(); diff --git a/src/middlewares/existance.js b/src/middlewares/existance.js new file mode 100644 index 0000000..2b95efb --- /dev/null +++ b/src/middlewares/existance.js @@ -0,0 +1,21 @@ +import customError from '../response/customError.js'; +import responseCodes from '../response/responseCodes.js'; +import GroupService from '../services/group.js'; + +const groupExists = async (req, res, next) => { + let groupId = req.params.groupId || req.body.groupId; + + let group = await GroupService.getById(groupId); + + if (!group) throw new customError(`group does not exists`, responseCodes.responses.groups.id_not_found); + next(); +}; + +const localIdExists = async (req, res, next) => { + let localId = req.params.localId || req.body.localId; + + if (!localId) throw new customError(`local id is not specified`, responseCodes.responses.general.invalid_syntax); + next(); +} + +export default { groupExists, localIdExists } \ No newline at end of file diff --git a/src/response/errorHandler.js b/src/response/errorHandler.js index f287926..646ca7f 100644 --- a/src/response/errorHandler.js +++ b/src/response/errorHandler.js @@ -1,6 +1,6 @@ -import log from "../utils/log"; -import customError from "./customError"; -import responses from './responseCodes'; +import log from "../utils/log.js"; +import customError from "./customError.js"; +import responses from './responseCodes.js'; import translate from '../utils/translate.js'; const errorHandler = (err, req, res) => { @@ -9,7 +9,7 @@ const errorHandler = (err, req, res) => { let language = req.headers["accept-language"]; if (err instanceof customError) { - return res.status(responses.getHTTPCode(err.code)).send(translate(language, code)); + return res.status(responses.getHTTPCode(err.code)).send(translate(language, err.code)); } } diff --git a/src/response/responseCodes.js b/src/response/responseCodes.js index b202621..2f8e530 100644 --- a/src/response/responseCodes.js +++ b/src/response/responseCodes.js @@ -1,5 +1,3 @@ -import fs from 'fs'; - const responses = { authentication: { not_found: "authentication not found", @@ -8,7 +6,8 @@ const responses = { user: { duplicate: "user duplicate", invalid_syntax: "user invalid syntax", - already_in_group: "user already in group" + already_in_group: "user already in group", + not_found: "user not found" }, usernames: { duplicate: "username taken", @@ -28,8 +27,6 @@ const responses = { }, abstractproducts: { not_found: "abstract product not found", - hash_without_file: "hash without file", - file_without_hash: "file without hash", invalid_syntax: "abstract product invalid syntax" }, barcode: { @@ -95,7 +92,7 @@ const responses = { } const getHTTPCode = (type) => { - switch (code) { + switch (type) { case responses.general.ok: return 200 case responses.general.invalid_syntax: @@ -118,6 +115,8 @@ const getHTTPCode = (type) => { return 409 case responses.user.invalid_syntax: return 400 + case responses.user.not_found: + return 404 case responses.usernames.duplicate: @@ -145,10 +144,6 @@ const getHTTPCode = (type) => { case responses.abstractproducts.not_found: return 404 - case responses.abstractproducts.file_without_hash: - return 400 - case responses.abstractproducts.hash_without_file: - return 400 case responses.abstractproducts.invalid_syntax: return 400 @@ -166,15 +161,6 @@ const getHTTPCode = (type) => { case responses.abstractproductname.too_long: return 400 - case responses.abstractproducts.file_without_hash: - return 400 - case responses.abstractproducts.hash_without_file: - return 400 - case responses.abstractproducts.invalid_syntax: - return 400 - case responses.abstractproducts.not_found: - return 404 - case responses.amount.invalid_syntax: return 400 case responses.amount.too_long: diff --git a/src/routers/abstractproduct.js b/src/routers/abstractproduct.js index a043cce..1cf664c 100644 --- a/src/routers/abstractproduct.js +++ b/src/routers/abstractproduct.js @@ -4,6 +4,7 @@ import AbstractProductController from '../controllers/abstractproduct.js'; import multer from 'multer'; import path from 'path'; import tryHandler from '../response/errorHandler.js'; +import existance from '../middlewares/existance.js'; const upload = multer(({ dest: path.join(path.resolve(path.dirname('')), "/temp") @@ -12,6 +13,6 @@ const upload = multer(({ const AbstractProductRouter = new Router(); AbstractProductRouter.post('/create', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(AbstractProductController.create)); -AbstractProductRouter.post('/update', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(AbstractProductController.update)); +AbstractProductRouter.post('/update', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(AbstractProductController.update)); export default AbstractProductRouter; \ No newline at end of file diff --git a/src/routers/category.js b/src/routers/category.js index 07f7839..8a5da77 100644 --- a/src/routers/category.js +++ b/src/routers/category.js @@ -2,10 +2,11 @@ import { Router } from 'express'; import auth from '../middlewares/auth.js'; import CategoryController from '../controllers/category.js'; import tryHandler from '../response/errorHandler.js'; +import existance from '../middlewares/existance.js'; const CategoryRouter = new Router(); -CategoryRouter.post('/create', tryHandler(auth.authenticate), tryHandler(CategoryController.create)); -CategoryRouter.post('/update', tryHandler(auth.authenticate), tryHandler(CategoryController.update)); +CategoryRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(CategoryController.create)); +CategoryRouter.post('/update', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(CategoryController.update)); export default CategoryRouter; \ No newline at end of file diff --git a/src/routers/group.js b/src/routers/group.js index e27571a..e3c4cd0 100644 --- a/src/routers/group.js +++ b/src/routers/group.js @@ -2,11 +2,12 @@ import { Router } from 'express'; import auth from '../middlewares/auth.js'; import GroupController from '../controllers/group.js'; import tryHandler from '../response/errorHandler.js'; +import existance from '../middlewares/existance.js'; const GroupRouter = new Router(); GroupRouter.post('/create/:groupName', tryHandler(auth.authenticate), tryHandler(GroupController.create)); -GroupRouter.post('/join/:groupId', tryHandler(auth.authenticate), tryHandler(auth.requirePassword), tryHandler(auth.checkGroupPassword), tryHandler(GroupController.join)); -GroupRouter.post('/password/:groupId', tryHandler(auth.authenticate), tryHandler(auth.authorizeGroupOwner), tryHandler(auth.requirePassword), tryHandler(GroupController.updatePassword)); +GroupRouter.post('/join/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.requirePassword), tryHandler(auth.checkGroupPassword), tryHandler(GroupController.join)); +GroupRouter.post('/password/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.authorizeGroupOwner), tryHandler(auth.requirePassword), tryHandler(GroupController.updatePassword)); export default GroupRouter; \ No newline at end of file diff --git a/src/routers/product.js b/src/routers/product.js index 07d338e..42439c1 100644 --- a/src/routers/product.js +++ b/src/routers/product.js @@ -2,10 +2,11 @@ import { Router } from 'express'; import auth from '../middlewares/auth.js'; import ProductController from '../controllers/product.js' import tryHandler from '../response/errorHandler.js'; +import existance from '../middlewares/existance.js'; const ProductRouter = new Router(); -ProductRouter.post('/create', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(ProductController.create)); -ProductRouter.post('/update', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(ProductController.update)); +ProductRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.create)); +ProductRouter.post('/update', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.update)); export default ProductRouter; \ No newline at end of file diff --git a/src/services/abstractproduct.js b/src/services/abstractproduct.js index 9362598..e32071c 100644 --- a/src/services/abstractproduct.js +++ b/src/services/abstractproduct.js @@ -1,7 +1,5 @@ import db from '../db.js'; -import statuses from '../utils/status.js'; import errorHandler from '../utils/pgerrorhandler.js'; -import tryHandler from '../response/errorHandler.js'; import responses from '../response/responseCodes.js'; import customError from '../response/customError.js'; diff --git a/src/services/category.js b/src/services/category.js index 73cce2d..f7337a8 100644 --- a/src/services/category.js +++ b/src/services/category.js @@ -1,7 +1,6 @@ import db from '../db.js'; import customError from '../response/customError.js'; import responseCodes from '../response/responseCodes.js'; -import statuses from '../utils/status.js'; class CategoryService { async create(groupId, categoryId, name) { diff --git a/src/services/group.js b/src/services/group.js index de2e560..c258254 100644 --- a/src/services/group.js +++ b/src/services/group.js @@ -5,14 +5,14 @@ import errorHandler from '../utils/pgerrorhandler.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) => errorHandler(e, "group")); + let res = await db.query("INSERT INTO groups (name, admin_id) VALUES ($1, $2) RETURNING ID", [name, creatorId]).catch((e) => errorHandler(e, "groups")); return res.rows[0]; } async getById(id) { let res = (await db.query("SELECT * FROM groups WHERE id = $1", [id])); - if (res.rowCount == 0) throw new customError(`getByUd group not found`, responseCodes.responses.groups.id_not_found); + if (res.rowCount == 0) throw new customError(`getById group not found`, responseCodes.responses.groups.id_not_found); return res.rows[0]; } diff --git a/src/services/user.js b/src/services/user.js index b0b48f7..5b16367 100644 --- a/src/services/user.js +++ b/src/services/user.js @@ -1,21 +1,20 @@ import db from '../db.js' import customError from '../response/customError.js'; import responseCodes from '../response/responseCodes.js'; -import statuses from '../utils/status.js'; import bcrypt from 'bcrypt'; +import errorHandler from '../utils/pgerrorhandler.js'; class UserService { 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; } async getByUsername(username) { - let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username])).rows; - if (user == undefined) throw new customError(`getByUsername user not found`, responseCodes.responses.usernames.not_found); - return (user[0]); + let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username])); + if (user.rowCount == 0) throw new customError(`getByUsername user not found`, responseCodes.responses.usernames.not_found); + return user.rows[0]; } async getAll() { @@ -31,7 +30,6 @@ class UserService { async joinGroup(userId, groupId) { if (await (this.isInGroup(userId, groupId))) throw new customError(`joinGroup user already in group`, responseCodes.responses.user.already_in_group); await db.query("UPDATE Users SET groups = array_append(groups, $1::integer) WHERE ID = $2", [groupId, userId]); - return statuses.ok; } } diff --git a/src/utils/pgerrorhandler.js b/src/utils/pgerrorhandler.js index 5035dc7..8b19cad 100644 --- a/src/utils/pgerrorhandler.js +++ b/src/utils/pgerrorhandler.js @@ -4,7 +4,7 @@ import responseCodes from "../response/responseCodes.js"; const errorHandler = (e, obj) => { switch (e.code) { case '23505': - throw new customError(`Duplicate ${obj}`, responseCodes.responses[obs].duplicate) + throw new customError(`Duplicate ${obj}`, responseCodes.responses[obj].duplicate) case '22007': throw new customError(`Invalid syntax ${obj}`, responseCodes.responses.general.invalid_syntax) case '22001': diff --git a/src/utils/translate.js b/src/utils/translate.js index 9aebbba..58ef609 100644 --- a/src/utils/translate.js +++ b/src/utils/translate.js @@ -1,7 +1,9 @@ +import fs from 'fs'; + const translate = (language, code) => { if (!language) language = "en-US" if (!code) code = "unknown" - return JSON.parse(fs.readFileSync(`../../messages/${language}/msgs.json`).toString())[code] + return JSON.parse(fs.readFileSync(`./messages/${language}/msgs.json`).toString())[code]; } export default translate \ No newline at end of file