Compare commits
49 Commits
09b5106538
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 010475bca2 | |||
| 63aaa30d92 | |||
| 947dc720a5 | |||
| 7d98ba89c5 | |||
| 9988cedbc4 | |||
| 4f5cd78eb0 | |||
| 0e4cc6507b | |||
| 1e6975fa13 | |||
| 05652f1cfb | |||
| 3c7e88d389 | |||
| eb0c8ac99d | |||
| 2be40b6f34 | |||
| c4f0d65f4f | |||
| 66bfba3b8e | |||
| 0608ccda6d | |||
| c39e8c9790 | |||
| 77d95824f3 | |||
| 757f5c13a5 | |||
| 7a89ce2840 | |||
| 5853023069 | |||
| 1a557fd530 | |||
| 646bc77aa0 | |||
| 1099738ac3 | |||
| 66fff16d30 | |||
| 96133b7733 | |||
| 693a0980d9 | |||
| 69c2255614 | |||
| 0b32306f84 | |||
| 3858a3c06e | |||
| c34cd7f809 | |||
| 317fba81aa | |||
| 07ffeb8de4 | |||
| c869f7c6ac | |||
| 02b1a572d1 | |||
| afa1edeb8d | |||
| f6e25d606a | |||
| 6adb48aeab | |||
| d809abd17b | |||
| 9cd1debd4a | |||
| d4233c72f5 | |||
| c950882e0e | |||
| ad5d7c4547 | |||
| c9e679ec4c | |||
| 7a2ab7dd5b | |||
| 47e90764e7 | |||
| 155faa6731 | |||
| f2945fc92d | |||
| 0e9a7f0856 | |||
| f720f20db0 |
7
.gitignore
vendored
7
.gitignore
vendored
@@ -134,4 +134,9 @@ dist
|
|||||||
#Draw.io
|
#Draw.io
|
||||||
|
|
||||||
.dtmp
|
.dtmp
|
||||||
.bkp
|
.bkp
|
||||||
|
|
||||||
|
#Local
|
||||||
|
uploads
|
||||||
|
temp
|
||||||
|
config.json
|
||||||
|
|||||||
7
Dockerfile
Normal file
7
Dockerfile
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
FROM node:22-bullseye
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
RUN npm i
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
ENTRYPOINT ["node", "./src/index.js"]
|
||||||
@@ -1,2 +1,9 @@
|
|||||||
# bsfe_server
|
# bsfe_server
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
1.Copy ``sample.config.json`` to ``config.json``
|
||||||
|
2. Edit ``config.json`` to your needs (port, database credentials)
|
||||||
|
3. Edit ``docker-compose.yml`` (insert changed port and database credentials from ``config.json``)
|
||||||
|
4. First-time: ``docker-compose up --build``
|
||||||
|
5. Then just: ``docker-compose up -d``
|
||||||
23
docker-compose.yml
Normal file
23
docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
bsfe:
|
||||||
|
build: .
|
||||||
|
|
||||||
|
ports:
|
||||||
|
- 8012:8080
|
||||||
|
- 8282:8282
|
||||||
|
depends_on:
|
||||||
|
- database
|
||||||
|
restart: on-failure
|
||||||
|
volumes:
|
||||||
|
- ./data/temp:/temp
|
||||||
|
- ./data/uploads:/uploads
|
||||||
|
database:
|
||||||
|
image: 'postgres:15'
|
||||||
|
volumes:
|
||||||
|
- ./data/db:/var/lib/postgresql/data
|
||||||
|
environment:
|
||||||
|
POSTGRES_USER: bsfe
|
||||||
|
POSTGRES_PASSWORD: Ch@NgEME!
|
||||||
|
POSTGRES_DB: bsfe
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "pg_isready", "-U", "bsfe"]
|
||||||
54
messages/en-US/msgs.json
Normal file
54
messages/en-US/msgs.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"authentication not found": "Authentication token not found!",
|
||||||
|
"authentication invalid": "Authentication token is invalid",
|
||||||
|
"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!",
|
||||||
|
"admin leave": "You are an admin of that group. You must transfer ownership of that group first!",
|
||||||
|
"username taken": "Username is taken!",
|
||||||
|
"username not found": "Such username not found!",
|
||||||
|
"username required": "Username is required!",
|
||||||
|
"password required": "Password is required!",
|
||||||
|
"password invalid": "Wrong password!",
|
||||||
|
"group name taken": "Group name is taken!",
|
||||||
|
"group name not found": "Group with such name not found!",
|
||||||
|
"group id not found": "Group with such ID not found!",
|
||||||
|
"group not an owner": "You are not an owner of this group!",
|
||||||
|
"group not a member": "You are not a member of this group!",
|
||||||
|
"name not specified": "New name of a group is not specified!",
|
||||||
|
"new owner not specified": "ID of a new group owner is not specified!",
|
||||||
|
"abstract product not found": "Such abstract product is not found!",
|
||||||
|
"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!",
|
||||||
|
"barcode too long": "This barcode is too long!",
|
||||||
|
"barcode invalid syntax": "Invalid syntax in barcode!",
|
||||||
|
"abstract product name not found": "Abstract product with such name not found!",
|
||||||
|
"abstract product name duplicate": "Abstract product with such name already exists!",
|
||||||
|
"abstract product name too long": "Abstract product name is too long!",
|
||||||
|
"abstract product name invalid syntax": "Invalid syntax in abstract product name!",
|
||||||
|
"net weight too long": "Specified net weight is too long!",
|
||||||
|
"net weight invalid syntax": "Invalid syntax in specified net weight!",
|
||||||
|
"image hash too long": "Specified image hash is too long!",
|
||||||
|
"image hash invalid syntax": "Invalid syntax in specified image hash!",
|
||||||
|
"category too long": "Specified category is too long!",
|
||||||
|
"category invalid syntax": "Invalid syntax in specified category!",
|
||||||
|
"category not found": "Such category not found!",
|
||||||
|
"unit too long": "Specified unit is too long!",
|
||||||
|
"unit invalid syntax": "Invalid syntax in specified unit!",
|
||||||
|
"product not found": "Such product not found!",
|
||||||
|
"product invalid syntax": "Invalid syntax in one of product's parameters!",
|
||||||
|
"abstract product id not found": "Specified abstract product id not found!",
|
||||||
|
"abstract product id invalid syntax": "Invalid syntax in abstract product id!",
|
||||||
|
"expiry date too long": "Specified expiry date is too long!",
|
||||||
|
"expiry date invalid syntax": "Invalid syntax in specified expiry date!",
|
||||||
|
"categories not found": "Such category not found!",
|
||||||
|
"categories duplicate": "Such category already exists!",
|
||||||
|
"categories invalid syntax": "Invalid syntax in one of category parameters!",
|
||||||
|
"ok": "Successfull.",
|
||||||
|
"invalid syntax": "Invalid syntax!",
|
||||||
|
"png only": "Only .png files are allowed!",
|
||||||
|
"too long": "Specified value is too long!",
|
||||||
|
"unknown": "Unknown server error. Please, contact the develoeper!"
|
||||||
|
}
|
||||||
54
messages/ru-RU/msgs.json
Normal file
54
messages/ru-RU/msgs.json
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
"authentication not found": "Токен аутентификации не найден!",
|
||||||
|
"authentication invalid": "Токен аутентификации неверен!",
|
||||||
|
"user duplicate": "Такой пользователь уже существует!",
|
||||||
|
"user invalid syntax": "Неправильный синткасис в одном из параметров пользователя!",
|
||||||
|
"user already in group": "Пользователь уже в группе!",
|
||||||
|
"user not found": "Пользователь не существует!",
|
||||||
|
"admin leave": "Вы являетесь администратором группы. Перед выходом передайте владение группой!",
|
||||||
|
"username taken": "Имя пользователя занято!",
|
||||||
|
"username not found": "Такое имя пользователя не найдено!",
|
||||||
|
"username required": "Требуется имя пользователя!",
|
||||||
|
"password required": "Требуется пароль!",
|
||||||
|
"password invalid": "Неверный пароль!",
|
||||||
|
"group name taken": "Такое имя группы уже существует!",
|
||||||
|
"group name not found": "Группы с таким именем не существует!",
|
||||||
|
"group id not found": "Группы с таким ID не существует!",
|
||||||
|
"group not an owner": "Вы не владелец этой группы!",
|
||||||
|
"group not a member": "Вы не участник этой группы!",
|
||||||
|
"name not specified": "Не указано новое имя группы!",
|
||||||
|
"new owner not specified": "Не указан ID нового владельца группы!",
|
||||||
|
"abstract product not found": "Такой абстрактный продукт не найден!",
|
||||||
|
"abstract product invalid syntax": "Неправильный синткасис в одном из параметров абстрактного продукта!",
|
||||||
|
"barcode not found": "Такой штрихкод не найден!",
|
||||||
|
"barcode duplicate": "Такой штрихкод уже существует!",
|
||||||
|
"barcode too long": "Этот штрихкод слишком большой!",
|
||||||
|
"barcode invalid syntax": "Этот штрихкод неверен!",
|
||||||
|
"abstract product name not found": "абстрактного продукта с таким именем не найдено!",
|
||||||
|
"abstract product name duplicate": "абстрактный продукт с таким именем уже существует!",
|
||||||
|
"abstract product name too long": "Имя абстрактного продукта слишком длинное!",
|
||||||
|
"abstract product name invalid syntax": "Неверный синткасис в имени абстрактного продукта!",
|
||||||
|
"net weight too long": "Указанный вес нетто слишком длинный!",
|
||||||
|
"net weight invalid syntax": "Неверный указанный вес нетто!",
|
||||||
|
"image hash too long": "Указанный хеш изображения слишком длинный!",
|
||||||
|
"image hash invalid syntax": "Неверный указанный хеш изображения!",
|
||||||
|
"category too long": "Указанная категория слишком длинная!",
|
||||||
|
"category invalid syntax": "Неверный синткасис в категории!",
|
||||||
|
"category not found": "Такая категория не найдена!!",
|
||||||
|
"unit too long": "Указанная единица измерения слишком длинная!",
|
||||||
|
"unit invalid syntax": "Неправильный синткасис в указанной единице измерения!",
|
||||||
|
"product not found": "Такой продукт не найден!",
|
||||||
|
"product invalid syntax": "Неверный синткасис в одном из параметров продукта!",
|
||||||
|
"abstract product id not found": "Абстрактный продукт с таким ID не найден!",
|
||||||
|
"abstract product id invalid syntax": "Неверный синткасис ID абстрактного продукта!",
|
||||||
|
"expiry date too long": "Указанная дата годен до слишком длинная!",
|
||||||
|
"expiry date invalid syntax": "Неверный синткасис в указанной дате годен до!",
|
||||||
|
"categories not found": "Накая категория не найдена!",
|
||||||
|
"categories duplicate": "Такая категория уже существует!",
|
||||||
|
"categories invalid syntax": "Неверный синткасис одного из параметров категории!",
|
||||||
|
"ok": "Успешно.",
|
||||||
|
"invalid syntax": "Неправильный синткасис!",
|
||||||
|
"png only": "Разрешены только файлы формата .png!",
|
||||||
|
"too long": "Указанное значение слишком длинное!",
|
||||||
|
"unknown": "Неизвестная ошибка сервера! Пожалуйста, сообщите разработчику!"
|
||||||
|
}
|
||||||
392
package-lock.json
generated
392
package-lock.json
generated
@@ -12,8 +12,12 @@
|
|||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodemon": "^3.1.7",
|
"nodemon": "^3.1.7",
|
||||||
"pg": "^8.13.1"
|
"path": "^0.12.7",
|
||||||
|
"pg": "^8.13.1",
|
||||||
|
"websocket": "^1.0.35",
|
||||||
|
"ws": "^8.18.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@mapbox/node-pre-gyp": {
|
"node_modules/@mapbox/node-pre-gyp": {
|
||||||
@@ -89,6 +93,12 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/aproba": {
|
"node_modules/aproba": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz",
|
||||||
@@ -214,6 +224,36 @@
|
|||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/bufferutil": {
|
||||||
|
"version": "4.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/bufferutil/-/bufferutil-4.0.8.tgz",
|
||||||
|
"integrity": "sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "^4.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"streamsearch": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@@ -290,6 +330,51 @@
|
|||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "1.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
|
||||||
|
"integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 0.8"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^2.2.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/concat-stream/node_modules/readable-stream": {
|
||||||
|
"version": "2.3.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||||
|
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"core-util-is": "~1.0.0",
|
||||||
|
"inherits": "~2.0.3",
|
||||||
|
"isarray": "~1.0.0",
|
||||||
|
"process-nextick-args": "~2.0.0",
|
||||||
|
"safe-buffer": "~5.1.1",
|
||||||
|
"string_decoder": "~1.1.1",
|
||||||
|
"util-deprecate": "~1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/concat-stream/node_modules/safe-buffer": {
|
||||||
|
"version": "5.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||||
|
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/concat-stream/node_modules/string_decoder": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/console-control-strings": {
|
"node_modules/console-control-strings": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
|
||||||
@@ -332,6 +417,25 @@
|
|||||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/core-util-is": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/d": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/d/-/d-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"es5-ext": "^0.10.64",
|
||||||
|
"type": "^2.7.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/debug": {
|
"node_modules/debug": {
|
||||||
"version": "4.3.7",
|
"version": "4.3.7",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||||
@@ -451,12 +555,67 @@
|
|||||||
"node": ">= 0.4"
|
"node": ">= 0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/es5-ext": {
|
||||||
|
"version": "0.10.64",
|
||||||
|
"resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz",
|
||||||
|
"integrity": "sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"es6-iterator": "^2.0.3",
|
||||||
|
"es6-symbol": "^3.1.3",
|
||||||
|
"esniff": "^2.0.1",
|
||||||
|
"next-tick": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es6-iterator": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "^0.10.35",
|
||||||
|
"es6-symbol": "^3.1.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/es6-symbol": {
|
||||||
|
"version": "3.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz",
|
||||||
|
"integrity": "sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d": "^1.0.2",
|
||||||
|
"ext": "^1.7.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/escape-html": {
|
"node_modules/escape-html": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/esniff": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"d": "^1.0.1",
|
||||||
|
"es5-ext": "^0.10.62",
|
||||||
|
"event-emitter": "^0.3.5",
|
||||||
|
"type": "^2.7.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/etag": {
|
"node_modules/etag": {
|
||||||
"version": "1.8.1",
|
"version": "1.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||||
@@ -466,6 +625,16 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/event-emitter": {
|
||||||
|
"version": "0.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
|
||||||
|
"integrity": "sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"d": "1",
|
||||||
|
"es5-ext": "~0.10.14"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/express": {
|
"node_modules/express": {
|
||||||
"version": "4.21.1",
|
"version": "4.21.1",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||||
@@ -523,6 +692,15 @@
|
|||||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/ext": {
|
||||||
|
"version": "1.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
|
||||||
|
"integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==",
|
||||||
|
"license": "ISC",
|
||||||
|
"dependencies": {
|
||||||
|
"type": "^2.7.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/fill-range": {
|
"node_modules/fill-range": {
|
||||||
"version": "7.1.1",
|
"version": "7.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||||
@@ -911,6 +1089,18 @@
|
|||||||
"node": ">=0.12.0"
|
"node": ">=0.12.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/is-typedarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/isarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/jsonwebtoken": {
|
"node_modules/jsonwebtoken": {
|
||||||
"version": "9.0.2",
|
"version": "9.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz",
|
||||||
@@ -1092,6 +1282,15 @@
|
|||||||
"node": "*"
|
"node": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/minimist": {
|
||||||
|
"version": "1.2.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
|
||||||
|
"integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/minipass": {
|
"node_modules/minipass": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
|
||||||
@@ -1144,6 +1343,36 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/multer": {
|
||||||
|
"version": "1.4.5-lts.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.1.tgz",
|
||||||
|
"integrity": "sha512-ywPWvcDMeH+z9gQq5qYHCCy+ethsk4goepZ45GLD63fOu0YcNecQxi64nDs3qluZB+murG3/D4dJ7+dGctcCQQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^1.0.0",
|
||||||
|
"concat-stream": "^1.5.2",
|
||||||
|
"mkdirp": "^0.5.4",
|
||||||
|
"object-assign": "^4.1.1",
|
||||||
|
"type-is": "^1.6.4",
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/multer/node_modules/mkdirp": {
|
||||||
|
"version": "0.5.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
|
"integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"minimist": "^1.2.6"
|
||||||
|
},
|
||||||
|
"bin": {
|
||||||
|
"mkdirp": "bin/cmd.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
@@ -1153,6 +1382,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/next-tick": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/node-addon-api": {
|
"node_modules/node-addon-api": {
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||||
@@ -1179,6 +1414,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/node-gyp-build": {
|
||||||
|
"version": "4.8.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.3.tgz",
|
||||||
|
"integrity": "sha512-EMS95CMJzdoSKoIiXo8pxKoL8DYxwIZXYlLmgPb8KUv794abpnLK6ynsCAWNliOjREKruYKdzbh76HHYUHX7nw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"bin": {
|
||||||
|
"node-gyp-build": "bin.js",
|
||||||
|
"node-gyp-build-optional": "optional.js",
|
||||||
|
"node-gyp-build-test": "build-test.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/nodemon": {
|
"node_modules/nodemon": {
|
||||||
"version": "3.1.7",
|
"version": "3.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
|
||||||
@@ -1295,6 +1541,16 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/path": {
|
||||||
|
"version": "0.12.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/path/-/path-0.12.7.tgz",
|
||||||
|
"integrity": "sha512-aXXC6s+1w7otVF9UletFkFcDsJeO7lSZBPUQhtb5O0xJe8LtYhj/GxldoL09bBj9+ZmE2hNoHqQSFMN5fikh4Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"process": "^0.11.1",
|
||||||
|
"util": "^0.10.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-is-absolute": {
|
"node_modules/path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
@@ -1450,6 +1706,21 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/process": {
|
||||||
|
"version": "0.11.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||||
|
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.6.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/process-nextick-args": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/proxy-addr": {
|
"node_modules/proxy-addr": {
|
||||||
"version": "2.0.7",
|
"version": "2.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||||
@@ -1734,6 +2005,14 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string_decoder": {
|
"node_modules/string_decoder": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
@@ -1834,6 +2113,12 @@
|
|||||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/type": {
|
||||||
|
"version": "2.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/type/-/type-2.7.3.tgz",
|
||||||
|
"integrity": "sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/type-is": {
|
"node_modules/type-is": {
|
||||||
"version": "1.6.18",
|
"version": "1.6.18",
|
||||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||||
@@ -1847,6 +2132,21 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/typedarray-to-buffer": {
|
||||||
|
"version": "3.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz",
|
||||||
|
"integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"is-typedarray": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/undefsafe": {
|
"node_modules/undefsafe": {
|
||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||||
@@ -1862,12 +2162,40 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/utf-8-validate": {
|
||||||
|
"version": "5.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz",
|
||||||
|
"integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"node-gyp-build": "^4.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/util": {
|
||||||
|
"version": "0.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/util/-/util-0.10.4.tgz",
|
||||||
|
"integrity": "sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "2.0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/util-deprecate": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/util/node_modules/inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
@@ -1892,6 +2220,38 @@
|
|||||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||||
"license": "BSD-2-Clause"
|
"license": "BSD-2-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/websocket": {
|
||||||
|
"version": "1.0.35",
|
||||||
|
"resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz",
|
||||||
|
"integrity": "sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"debug": "^2.2.0",
|
||||||
|
"es5-ext": "^0.10.63",
|
||||||
|
"typedarray-to-buffer": "^3.1.5",
|
||||||
|
"utf-8-validate": "^5.0.2",
|
||||||
|
"yaeti": "^0.0.6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/websocket/node_modules/debug": {
|
||||||
|
"version": "2.6.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||||
|
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"ms": "2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/websocket/node_modules/ms": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/whatwg-url": {
|
"node_modules/whatwg-url": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||||
@@ -1917,6 +2277,27 @@
|
|||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
|
"node_modules/ws": {
|
||||||
|
"version": "8.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
|
||||||
|
"integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"bufferutil": "^4.0.1",
|
||||||
|
"utf-8-validate": ">=5.0.2"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"bufferutil": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"utf-8-validate": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/xtend": {
|
"node_modules/xtend": {
|
||||||
"version": "4.0.2",
|
"version": "4.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||||
@@ -1926,6 +2307,15 @@
|
|||||||
"node": ">=0.4"
|
"node": ">=0.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/yaeti": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.32"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/yallist": {
|
"node_modules/yallist": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
|
|||||||
@@ -16,8 +16,12 @@
|
|||||||
"bcrypt": "^5.1.1",
|
"bcrypt": "^5.1.1",
|
||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
|
"multer": "^1.4.5-lts.1",
|
||||||
"nodemon": "^3.1.7",
|
"nodemon": "^3.1.7",
|
||||||
"pg": "^8.13.1"
|
"path": "^0.12.7",
|
||||||
|
"pg": "^8.13.1",
|
||||||
|
"websocket": "^1.0.35",
|
||||||
|
"ws": "^8.18.0"
|
||||||
},
|
},
|
||||||
"type": "module"
|
"type": "module"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"debug": true,
|
"debug": true,
|
||||||
"port": "8081",
|
"port": "8081",
|
||||||
|
"wsport": "8282",
|
||||||
"dbuser": "bsfe",
|
"dbuser": "bsfe",
|
||||||
"dbhost": "localhost",
|
"dbhost": "localhost",
|
||||||
"dbport": "5432",
|
"dbport": "5432",
|
||||||
@@ -1,54 +1,115 @@
|
|||||||
import AbstractProductService from '../services/abstractproduct.js';
|
import AbstractProductService from '../services/abstractproduct.js';
|
||||||
import statuses from '../utils/status.js';
|
import ProductService from '../services/product.js';
|
||||||
import log from '../utils/log.js';
|
import fs from 'fs';
|
||||||
|
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';
|
||||||
|
import notify from '../utils/notify.js';
|
||||||
|
|
||||||
const TAG = "/controllers/abstractproduct.js";
|
const TAG = "/controllers/abstractproduct.js";
|
||||||
|
|
||||||
class AbstractProductController {
|
class AbstractProductController {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
const { groupId, localId, barcode, name, net_weight, category, unit } = req.body;
|
||||||
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);
|
if (!req.file) throw new customError(`user hasn't supplied a file for abstract product creation`, responseCodes.responses.image_filename)
|
||||||
return res.status(200).send("Successfull");
|
|
||||||
} catch (e) {
|
const tempPath = req.file.path;
|
||||||
switch (e.status) {
|
const image_buffer = fs.readFileSync(tempPath);
|
||||||
case statuses.duplicate:
|
const image_filename = createHash('md5').update(image_buffer).digest('hex');
|
||||||
return res.status(400).send(e.message);
|
const targetPath = path.join(path.resolve(path.dirname('')), `/uploads/${image_filename}.png`);
|
||||||
default:
|
|
||||||
log.error(e.original);
|
if (path.extname(req.file.originalname).toLowerCase() !== ".png") {
|
||||||
return res.status(500).send(e.message);
|
fs.rmSync(tempPath);
|
||||||
}
|
throw new customError(`create abstract product only png allowed`, responseCodes.responses.general.png_only);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fs.copyFileSync(tempPath, targetPath);
|
||||||
|
fs.rmSync(tempPath);
|
||||||
|
|
||||||
|
await AbstractProductService.create(groupId, localId, barcode, name, net_weight, image_filename, category, unit);
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'create', 'abstractproduct', {
|
||||||
|
local_id: localId, barcode, name, net_weight, image_filename, category, unit
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(req, res) {
|
async update(req, res) {
|
||||||
try {
|
let { groupId, localId, barcode, name, net_weight, category, unit } = req.body;
|
||||||
let { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body;
|
|
||||||
|
|
||||||
if (barcode) await AbstractProductService.updateBarcode(groupId, localId, barcode);
|
var tempPath, image_buffer, image_filename, targetPath;
|
||||||
|
|
||||||
if (name) await AbstractProductService.updateName(groupId, localId, name);
|
if (req.file) {
|
||||||
|
tempPath = req.file.path;
|
||||||
|
|
||||||
if (net_weight) await AbstractProductService.updateNetWeight(groupId, localId, net_weight);
|
let previousImageHash = (await AbstractProductService.getByLocalId(groupId, localId)).image_filename
|
||||||
|
let previousImagePath = path.join(path.resolve(path.dirname('')), `/uploads/${previousImageHash}.png`);
|
||||||
|
fs.unlinkSync(previousImagePath)
|
||||||
|
|
||||||
if (image_filename) await AbstractProductService.updateImageFilename(groupId, localId, image_filename);
|
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 (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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 (tempPath) await AbstractProductService.updateImageFilename(groupId, localId, image_filename);
|
||||||
|
|
||||||
|
if (category) await AbstractProductService.updateCategory(groupId, localId, category);
|
||||||
|
|
||||||
|
if (unit) await AbstractProductService.updateUnit(groupId, localId, unit);
|
||||||
|
|
||||||
|
let data = await AbstractProductService.getByLocalId(groupId, localId)
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'update', 'abstractproduct', data);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getById(req, res) {
|
||||||
|
let { localId, groupId } = req.params
|
||||||
|
|
||||||
|
let result = await AbstractProductService.getByLocalId(groupId, localId)
|
||||||
|
|
||||||
|
return res.status(200).send(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
async getImage(req, res) {
|
||||||
|
let { localId, groupId } = req.params;
|
||||||
|
|
||||||
|
let imageFilename = (await AbstractProductService.getByLocalId(groupId, localId)).image_filename
|
||||||
|
let imagePath = path.join(path.resolve(path.dirname('')), `/uploads/${imageFilename}.png`);
|
||||||
|
|
||||||
|
let image = fs.readFileSync(imagePath)
|
||||||
|
|
||||||
|
return res.status(200).send(image)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(req, res) {
|
||||||
|
let { localId, groupId } = req.params;
|
||||||
|
|
||||||
|
let imageFilename = (await AbstractProductService.getByLocalId(groupId, localId)).image_filename
|
||||||
|
let imagePath = path.join(path.resolve(path.dirname('')), `/uploads/${imageFilename}.png`);
|
||||||
|
fs.unlinkSync(imagePath)
|
||||||
|
|
||||||
|
await AbstractProductService.delete(groupId, localId)
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'delete', 'abstractproduct', { local_id: localId });
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default new AbstractProductController();
|
export default new AbstractProductController();
|
||||||
|
|||||||
@@ -1,25 +1,51 @@
|
|||||||
import CategoryService from "../services/category.js";
|
import CategoryService from "../services/category.js";
|
||||||
import log from "../utils/log.js";
|
import translate from "../utils/translate.js";
|
||||||
|
import responseCodes from "../response/responseCodes.js";
|
||||||
|
import notify from "../utils/notify.js";
|
||||||
|
|
||||||
const TAG = "controllers/category.js";
|
const TAG = "controllers/category.js";
|
||||||
|
|
||||||
class CategoryController {
|
class CategoryController {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
const { localId, categoryName, groupId } = req.body;
|
||||||
const { localId, categoryName, groupId } = req.body;
|
|
||||||
|
|
||||||
await CategoryService.create(groupId, localId, categoryName);
|
await CategoryService.create(groupId, localId, categoryName);
|
||||||
return res.status(200).send("Success");
|
|
||||||
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/create: ${e}`)); }
|
notify(req.headers.authorization.split(' ')[1], groupId, 'create', 'category', {
|
||||||
|
local_id: localId, name: categoryName
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
}
|
}
|
||||||
|
|
||||||
async update(req, res) {
|
async update(req, res) {
|
||||||
try {
|
const { localId, groupId, categoryName } = req.body;
|
||||||
const { localId, groupId, name } = req.body;
|
|
||||||
|
|
||||||
await CategoryService.update(groupId, localId, name);
|
await CategoryService.update(groupId, localId, categoryName);
|
||||||
return res.status(200).send("Success");
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/update: ${e}`)); }
|
let data = await CategoryService.getById(groupId, localId)
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'update', 'category', data);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByLocalId(req, res) {
|
||||||
|
const { groupId, localId } = req.params;
|
||||||
|
|
||||||
|
let result = await CategoryService.getById(groupId, localId)
|
||||||
|
|
||||||
|
return res.status(200).send(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(req, res) {
|
||||||
|
const { groupId, localId } = req.params;
|
||||||
|
|
||||||
|
await CategoryService.delete(groupId, localId);
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'delete', 'category', { local_id: localId });
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -2,50 +2,113 @@ import GroupService from '../services/group.js';
|
|||||||
import UserService from '../services/user.js';
|
import UserService from '../services/user.js';
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import config from '../../config.json' with {type: "json"};
|
import config from '../../config.json' with {type: "json"};
|
||||||
import statuses from '../utils/status.js';
|
|
||||||
import log from '../utils/log.js';
|
import log from '../utils/log.js';
|
||||||
|
import translate from '../utils/translate.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
|
||||||
const TAG = "/controllers/group.js";
|
const TAG = "/controllers/group.js";
|
||||||
|
|
||||||
class GroupController {
|
class GroupController {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
let { groupName } = req.params;
|
||||||
let { groupName } = req.params;
|
|
||||||
|
|
||||||
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
||||||
let status = await GroupService.create(groupName, user.login.id);
|
let group = await GroupService.create(groupName, user.login.id);
|
||||||
|
|
||||||
log.info(`New group with name ${groupName} was just created by user ${user.login.username}`);
|
await UserService.joinGroup(user.login.id, group.id);
|
||||||
|
return res.status(200).send(group.id.toString())
|
||||||
UserService.joinGroup(user.login.id, status.id);
|
|
||||||
return res.status(200).send("Successfull");
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/create: ${e}`)); }
|
|
||||||
}
|
}
|
||||||
async join(req, res) {
|
async join(req, res) {
|
||||||
try {
|
let { groupId } = req.params;
|
||||||
let { groupId } = req.params;
|
|
||||||
|
|
||||||
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
||||||
let status = await UserService.joinGroup(user.login.id, groupId);
|
await UserService.joinGroup(user.login.id, groupId);
|
||||||
|
|
||||||
if (status == statuses.duplicate) return res.status(400).send("Already in group");
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
log.info(`User ${user.login.username} has just joined group with ID ${groupId}`);
|
async leave(req, res) {
|
||||||
return res.status(200).send("Successfull");
|
let groupId = req.params.groupId;
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/join: ${e}`)); }
|
|
||||||
|
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
||||||
|
|
||||||
|
if (await GroupService.getAdminId(groupId) == user.login.id) {
|
||||||
|
let code = responseCodes.responses.user.admin_leave
|
||||||
|
return res.status(responseCodes.getHTTPCode(code)).send(translate(req.headers["accept-language"], code))
|
||||||
|
}
|
||||||
|
|
||||||
|
await UserService.leaveGroup(user.login.id, groupId);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
}
|
}
|
||||||
|
|
||||||
async updatePassword(req, res) {
|
async updatePassword(req, res) {
|
||||||
try {
|
let { groupId } = req.params;
|
||||||
let { groupId } = req.params;
|
let { password } = req.body;
|
||||||
let { password } = req.body;
|
|
||||||
|
|
||||||
await GroupService.updatePassword(groupId, password);
|
await GroupService.updatePassword(groupId, password);
|
||||||
log.info(`Password for group with ID ${groupId} was updated`);
|
log.info(`Password for group with ID ${groupId} was updated`);
|
||||||
return res.status(200).send("Successfull");
|
|
||||||
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/updatePassword ${e}`)); }
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByName(req, res) {
|
||||||
|
let groupName = req.params.groupName || req.body.groupName;
|
||||||
|
|
||||||
|
let group = await GroupService.getByName(groupName);
|
||||||
|
|
||||||
|
return res.status(200).send(group["id"].toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
async getById(req, res) {
|
||||||
|
let groupId = req.params.groupId;
|
||||||
|
|
||||||
|
let groupName = (await GroupService.getById(groupId)).name;
|
||||||
|
|
||||||
|
return res.status(200).send(groupName);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUsersInGroup(req, res) {
|
||||||
|
|
||||||
|
const groupId = req.params.groupId;
|
||||||
|
|
||||||
|
let result = await GroupService.getUsersInGroup(groupId);
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAdminId(req, res) {
|
||||||
|
const groupId = req.params.groupId;
|
||||||
|
|
||||||
|
let result = await GroupService.getAdminId(groupId);
|
||||||
|
|
||||||
|
if (!result) result = -1
|
||||||
|
|
||||||
|
return res.status(200).send(result.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
async rename(req, res) {
|
||||||
|
const groupId = req.params.groupId;
|
||||||
|
const name = req.body.name;
|
||||||
|
|
||||||
|
if (!name) throw new customError(`New group name is not specified`, responseCodes.responses.groups.name_not_specified);
|
||||||
|
|
||||||
|
await GroupService.rename(groupId, name);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
async transferOwnership(req, res) {
|
||||||
|
const groupId = req.params.groupId;
|
||||||
|
const userId = req.body.userId;
|
||||||
|
|
||||||
|
if (!userId) throw new customError(`New owner id is not specified`, responseCodes.responses.groups.new_owner_not_specified);
|
||||||
|
|
||||||
|
await GroupService.transferOwnership(groupId, userId);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new GroupController();
|
export default new GroupController();
|
||||||
|
|||||||
@@ -1,47 +1,58 @@
|
|||||||
import ProductService from '../services/product.js';
|
import ProductService from '../services/product.js';
|
||||||
import statuses from '../utils/status.js';
|
import translate from '../utils/translate.js';
|
||||||
import log from '../utils/log.js';
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
import notify from '../utils/notify.js';
|
||||||
const TAG = "/controllers/product.js"
|
const TAG = "/controllers/product.js"
|
||||||
|
|
||||||
class AbstractProductController {
|
class AbstractProductController {
|
||||||
async create(req, res) {
|
async create(req, res) {
|
||||||
try {
|
const { groupId, localId, abstract_product_id, amount, date_of_production, expiry_date } = req.body;
|
||||||
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);
|
await ProductService.create(groupId, localId, abstract_product_id, amount, date_of_production, expiry_date);
|
||||||
return res.status(200).send("Successfull");
|
|
||||||
} catch (e) {
|
let data = await ProductService.getByLocalId(groupId, localId)
|
||||||
switch (e.status) {
|
|
||||||
case statuses.duplicate:
|
notify(req.headers.authorization.split(' ')[1], groupId, 'create', 'product', data);
|
||||||
return res.status(400).send(e.message);
|
|
||||||
default:
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
log.error(e.original)
|
|
||||||
return res.status(500).send(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async update(req, res) {
|
async update(req, res) {
|
||||||
try {
|
let { groupId, localId, abstract_product_id, amount, date_of_production, expiry_date } = req.body;
|
||||||
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 (abstract_product_id) await ProductService.updateAbstractProductId(groupId, localId, abstract_product_id);
|
||||||
|
|
||||||
if (amount) await ProductService.updateAmount(groupId, localId, amount);
|
if (amount) await ProductService.updateAmount(groupId, localId, amount);
|
||||||
|
|
||||||
if (date_of_production) await ProductService.updateDateOfProduction(groupId, localId, date_of_production);
|
if (date_of_production) await ProductService.updateDateOfProduction(groupId, localId, date_of_production);
|
||||||
|
|
||||||
if (expiry_date) await ProductService.updateExpiryDate(groupId, localId, expiry_date);
|
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");
|
let data = await ProductService.getByLocalId(groupId, localId)
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'update', 'product', data);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByLocalId(req, res) {
|
||||||
|
let { groupId, localId } = req.params;
|
||||||
|
|
||||||
|
let result = await ProductService.getByLocalId(groupId, localId)
|
||||||
|
|
||||||
|
return res.status(200).send(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(req, res) {
|
||||||
|
let { groupId, localId } = req.params;
|
||||||
|
|
||||||
|
let id = (await ProductService.getByLocalId(groupId, localId)).id
|
||||||
|
|
||||||
|
await ProductService.delete(id)
|
||||||
|
|
||||||
|
notify(req.headers.authorization.split(' ')[1], groupId, 'delete', 'product', { local_id: localId });
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,47 +1,93 @@
|
|||||||
import UserService from '../services/user.js';
|
import UserService from '../services/user.js';
|
||||||
import log from '../utils/log.js';
|
import log from '../utils/log.js';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
import genToken from '../utils/jwt.js';
|
import jwtutils from '../utils/jwt.js';
|
||||||
import AbstractProductService from '../services/abstractproduct.js';
|
import AbstractProductService from '../services/abstractproduct.js';
|
||||||
import ProductService from '../services/product.js';
|
import ProductService from '../services/product.js';
|
||||||
|
import CategoryService from '../services/category.js';
|
||||||
|
import translate from '../utils/translate.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
import { createHash } from 'crypto';
|
||||||
|
|
||||||
const TAG = "/controllers/userjs"
|
const TAG = "/controllers/userjs"
|
||||||
|
|
||||||
class UserController {
|
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);
|
let userId = await UserService.create(username, password);
|
||||||
|
|
||||||
log.info(`New user with name ${username} has just registered`);
|
log.info(`New user with name ${username} has just registered`);
|
||||||
return res.status(200).send("Successfull register");
|
return res.status(200).send(userId.toString());
|
||||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/register: ${e}`)); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async login(req, res) {
|
async login(req, res) {
|
||||||
try {
|
const { username, password } = req.body;
|
||||||
const { username, password } = req.body;
|
|
||||||
|
|
||||||
const user = await UserService.getByUsername(username);
|
const user = await UserService.getByUsername(username);
|
||||||
if (!bcrypt.compareSync(password, user.password)) return res.status(401).send("Wrong password");
|
if (!bcrypt.compareSync(password, user.password)) throw new customError(`Wrong user password`, responseCodes.responses.passwords.invalid);
|
||||||
|
|
||||||
const token = genToken(user);
|
const token = jwtutils.genToken(user);
|
||||||
return res.status(200).send(token);
|
return res.status(200).send({ id: user.id, token: token });
|
||||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/login: ${e}`)); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async synchronize(req, res) {
|
async synchronize(req, res) {
|
||||||
try {
|
const { groupId } = req.params;
|
||||||
const { groupId } = req.params;
|
|
||||||
|
|
||||||
let result = {};
|
let result = {};
|
||||||
result.abstract_products = await AbstractProductService.getAll(groupId);
|
result.abstract_products = await AbstractProductService.getAll(groupId);
|
||||||
result.products = await ProductService.getAll(groupId);
|
result.abstract_products.forEach((abstractproduct) => {
|
||||||
result.categories = await CategoryService.getAll(groupId);
|
let data = `${abstractproduct.local_id}:${abstractproduct.barcode}:${abstractproduct.name}:${abstractproduct.net_weight}:${abstractproduct.image_filename}:${abstractproduct.category}:${abstractproduct.unit}`
|
||||||
|
abstractproduct.hash = createHash('md5').update(data).digest('hex');
|
||||||
|
});
|
||||||
|
|
||||||
return res.status(200).json(result);
|
result.products = await ProductService.getAll(groupId);
|
||||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/synchronize: ${e}`)); }
|
result.products.forEach((product) => {
|
||||||
|
let data = `${product.local_id}:${product.abstract_product_id}:${product.amount}:${new Date(product.date_of_production).valueOf() / 1000}:${new Date(product.expiry_date).valueOf() / 1000}`
|
||||||
|
product.hash = createHash('md5').update(data).digest('hex');
|
||||||
|
});
|
||||||
|
|
||||||
|
result.categories = await CategoryService.getAll(groupId);
|
||||||
|
result.categories.forEach((category) => {
|
||||||
|
let data = `${category.local_id}:${category.name}`
|
||||||
|
category.hash = createHash('md5').update(data).digest('hex');
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeUsername(req, res) {
|
||||||
|
const userId = jwtutils.getUserIdFromToken(req.headers.authorization.split(' ')[1]);
|
||||||
|
const { username } = req.body;
|
||||||
|
|
||||||
|
await UserService.changeUsername(userId, username);
|
||||||
|
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok))
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePassword(req, res) {
|
||||||
|
const userId = jwtutils.getUserIdFromToken(req.headers.authorization.split(' ')[1]);
|
||||||
|
const { password } = req.body;
|
||||||
|
|
||||||
|
await UserService.changePassword(userId, password);
|
||||||
|
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok))
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllGroupsForUser(req, res) {
|
||||||
|
const userId = jwtutils.getUserIdFromToken(req.headers.authorization.split(' ')[1]);
|
||||||
|
|
||||||
|
let result = await UserService.getAllGroupsForUser(userId);
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
async getById(req, res) {
|
||||||
|
const userId = req.params.userId
|
||||||
|
|
||||||
|
let result = (await UserService.getById(userId)).username;
|
||||||
|
|
||||||
|
return res.status(200).send(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import pg from 'pg';
|
import pg from 'pg';
|
||||||
import log from './utils/log.js'
|
import log from './utils/log.js'
|
||||||
import fs from 'fs'
|
import fs from 'fs';
|
||||||
import config from '../config.json' with {type: "json"};
|
import config from '../config.json' with {type: "json"};
|
||||||
|
|
||||||
const { Pool } = pg;
|
const { Pool } = pg;
|
||||||
|
|||||||
72
src/index.js
72
src/index.js
@@ -1,5 +1,6 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
import UserRouter from './routers/user.js';
|
import UserRouter from './routers/user.js';
|
||||||
|
import UserService from './services/user.js';
|
||||||
import GroupRouter from './routers/group.js';
|
import GroupRouter from './routers/group.js';
|
||||||
import AbstractProductRouter from './routers/abstractproduct.js';
|
import AbstractProductRouter from './routers/abstractproduct.js';
|
||||||
import log from './utils/log.js';
|
import log from './utils/log.js';
|
||||||
@@ -8,10 +9,16 @@ import config from '../config.json' with {type: "json"};
|
|||||||
import ProductRouter from './routers/product.js';
|
import ProductRouter from './routers/product.js';
|
||||||
import CategoryRouter from './routers/category.js';
|
import CategoryRouter from './routers/category.js';
|
||||||
|
|
||||||
const app = express();
|
import { WebSocketServer } from 'ws';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
app.use(express.urlencoded({ extended: true }));
|
const app = express();
|
||||||
app.use(express.json());
|
const wss = new WebSocketServer({ port: config.wsport });
|
||||||
|
|
||||||
|
const clients = [];
|
||||||
|
|
||||||
|
app.use(express.urlencoded({ extended: false, limit: "200mb", parameterLimit: 100000 }));
|
||||||
|
app.use(express.json({ limit: "200mb", parameterLimit: 1000000 }));
|
||||||
|
|
||||||
app.use('/api/user/', UserRouter);
|
app.use('/api/user/', UserRouter);
|
||||||
app.use('/api/group/', GroupRouter);
|
app.use('/api/group/', GroupRouter);
|
||||||
@@ -19,6 +26,59 @@ app.use('/api/abstractproduct', AbstractProductRouter);
|
|||||||
app.use('/api/product', ProductRouter);
|
app.use('/api/product', ProductRouter);
|
||||||
app.use('/api/category', CategoryRouter);
|
app.use('/api/category', CategoryRouter);
|
||||||
|
|
||||||
app.listen(config.port, () => {
|
app.get('/status', (req, res) => {
|
||||||
log.info(`Application has started on port ${config.port}`)
|
return res.status(200).send("All OK");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
wss.on('connection', (client) => {
|
||||||
|
client.on('message', async (message) => {
|
||||||
|
if (message == "keepalive") return;
|
||||||
|
|
||||||
|
let parsed = JSON.parse(message);
|
||||||
|
let token = parsed.token;
|
||||||
|
let currentGroup = parsed.currentGroup;
|
||||||
|
try {
|
||||||
|
if (!jwt.verify(token, config.secret)) {
|
||||||
|
client.send("Invalid token");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!await UserService.isInGroup(jwt.decode(token, config.secret).login.id, currentGroup)) {
|
||||||
|
client.send("Not a member of specified group");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
log.error("Error during connection through websocket.")
|
||||||
|
client.send("Error")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clients.push({
|
||||||
|
socket: client,
|
||||||
|
token,
|
||||||
|
currentGroup
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', () => {
|
||||||
|
for (let i = 0; i < clients.length; i++) {
|
||||||
|
if (clients[i].socket == client) {
|
||||||
|
log.debug(`Client with token ${clients[i].token} has disconnected`)
|
||||||
|
clients.splice(i, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const server = app.listen(config.port, () => {
|
||||||
|
log.info(`Application has started on port ${config.port}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
server.on('upgrade', (req, res, head) => {
|
||||||
|
wss.handleUpgrade(req, res, head, socket => {
|
||||||
|
wss.emit('connection', socket, req);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
export default clients;
|
||||||
@@ -1,73 +1,69 @@
|
|||||||
import log from '../utils/log.js'
|
|
||||||
import jwt from 'jsonwebtoken';
|
import jwt from 'jsonwebtoken';
|
||||||
import config from '../../config.json' with {type: "json"};
|
import config from '../../config.json' with {type: "json"};
|
||||||
import GroupService from '../services/group.js';
|
import GroupService from '../services/group.js';
|
||||||
import UserService from '../services/user.js';
|
import UserService from '../services/user.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
|
||||||
const TAG = "/middlewares/auth.js";
|
const TAG = "/middlewares/auth.js";
|
||||||
|
|
||||||
const requireUsername = async (req, res, next) => {
|
const requireUsername = async (req, res, next) => {
|
||||||
if (req.method == "OPTIONS") next();
|
if (req.method == "OPTIONS") next();
|
||||||
|
|
||||||
try {
|
const { username } = req.body;
|
||||||
const { username } = req.body;
|
if (!username) throw new customError(`requireUsername username is required`, responseCodes.responses.usernames.required)
|
||||||
if (!username) return res.status(400).send("Username is required");
|
next();
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requireUsername: ${e}`)); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const requirePassword = async (req, res, next) => {
|
const requirePassword = async (req, res, next) => {
|
||||||
if (req.method == "OPTIONS") next();
|
if (req.method == "OPTIONS") next();
|
||||||
|
|
||||||
try {
|
const { password } = req.body;
|
||||||
const { password } = req.body;
|
if (!password) throw new customError(`requirePassword password is required`, responseCodes.responses.passwords.required);
|
||||||
if (!password) return res.status(400).send("Password is required");
|
next();
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requirePassword: ${e}`)); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const authenticate = async (req, res, next) => {
|
const authenticate = async (req, res, next) => {
|
||||||
if (req.method == "OPTIONS") next();
|
if (req.method == "OPTIONS") next();
|
||||||
|
if (!req.headers.authorization) throw new customError(`authenticate no authorization header`, responseCodes.responses.authentication.not_found);
|
||||||
|
|
||||||
try {
|
const token = req.headers.authorization.split(' ')[1];
|
||||||
if (!req.headers.authorization) return res.status(403).send("No authorization header supplied");
|
if (!token) throw new customError(`authenticate no authorization token in header`, responseCodes.responses.authentication.not_found);
|
||||||
const token = req.headers.authorization.split(' ')[1];
|
|
||||||
if (!token) return res.status(403).send("No authorization token supplied");
|
|
||||||
if (!jwt.verify(token, config.secret)) return res.status(403).send("Authorization token is incorrect");
|
|
||||||
|
|
||||||
next();
|
let user = jwt.decode(token, config.secret);
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/authenticate: ${e}`)); }
|
|
||||||
|
await UserService.getByUsername(user.login.username)
|
||||||
|
|
||||||
|
if (!jwt.verify(token, config.secret)) throw new customError(`authenticate token is invalid`, responseCodes.responses.authentication.invalid);
|
||||||
|
|
||||||
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
const authorizeGroupOwner = async (req, res, next) => {
|
const authorizeGroupOwner = async (req, res, next) => {
|
||||||
if (req.method == "OPTIONS") next();
|
if (req.method == "OPTIONS") next();
|
||||||
|
|
||||||
try {
|
const token = req.headers.authorization.split(' ')[1];
|
||||||
const token = req.headers.authorization.split(' ')[1];
|
|
||||||
|
|
||||||
const { groupId } = req.params;
|
const { groupId } = req.params;
|
||||||
|
|
||||||
let user = jwt.decode(token, config.secret);
|
let user = jwt.decode(token, config.secret);
|
||||||
|
|
||||||
let adminId = await GroupService.getAdminId(groupId);
|
let adminId = await GroupService.getAdminId(groupId);
|
||||||
if (user.login.id != adminId) return res.status(403).send("Not your group");
|
if (user.login.id != adminId) throw new customError(`authorizeGroupOwner not an owner`, responseCodes.responses.groups.not_an_owner)
|
||||||
next();
|
next();
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/authorizeGroupOwner: ${e}`)); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkGroupPassword = async (req, res, next) => {
|
const checkGroupPassword = async (req, res, next) => {
|
||||||
if (req.method == "OPTIONS") next();
|
if (req.method == "OPTIONS") next();
|
||||||
|
|
||||||
try {
|
const { groupId } = req.params;
|
||||||
const { groupId } = req.params;
|
const { password } = req.body;
|
||||||
const { password } = req.body;
|
|
||||||
|
|
||||||
const groupPassword = await GroupService.getPassword(groupId);
|
const groupPassword = await GroupService.getPassword(groupId);
|
||||||
|
|
||||||
if (groupPassword != password) return res.status(403).send("Wrong password");
|
if (groupPassword != password) throw new customError(`checkGroupPassword password is invalid`, responseCodes.responses.passwords.invalid);
|
||||||
next();
|
next();
|
||||||
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/checkGroupPassword: ${e}`)); }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const userIsInGroup = async (req, res, next) => {
|
const userIsInGroup = async (req, res, next) => {
|
||||||
@@ -77,7 +73,7 @@ const userIsInGroup = async (req, res, next) => {
|
|||||||
|
|
||||||
const token = req.headers.authorization.split(' ')[1];
|
const token = req.headers.authorization.split(' ')[1];
|
||||||
let user = jwt.decode(token, config.secret);
|
let user = jwt.decode(token, config.secret);
|
||||||
if (!await UserService.isInGroup(user.login.id, groupId)) return res.status(403).send("You are not a member of this group");
|
if (!await UserService.isInGroup(user.login.id, groupId)) throw new customError(`userIsInGroup not a member`, responseCodes.responses.groups.not_a_member)
|
||||||
next();
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,116 +1,33 @@
|
|||||||
import UserService from '../services/user.js';
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
import GroupService from '../services/group.js';
|
import GroupService from '../services/group.js';
|
||||||
import AbstractProductService from '../services/abstractproduct.js';
|
|
||||||
import ProductService from '../services/product.js';
|
|
||||||
import CategoryService from '../services/category.js';
|
|
||||||
import log from '../utils/log.js';
|
|
||||||
import statuses from '../utils/status.js';
|
|
||||||
|
|
||||||
const TAG = "/middlewares/existance.js";
|
|
||||||
|
|
||||||
const usernameExists = async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
let { username } = req.body;
|
|
||||||
|
|
||||||
let user = await UserService.getByUsername(username);
|
|
||||||
if (!user || user == statuses.not_found) return res.status(404).send("User not found");
|
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/usernameExists: ${e}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const usernameDoesntExist = async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
let { username } = req.body;
|
|
||||||
|
|
||||||
let user = await UserService.getByUsername(username);
|
|
||||||
if (user != undefined && user != statuses.not_found) return res.status(400).send("Such username already taken");
|
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/usernameDoesntExist: ${e}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const groupExists = async (req, res, next) => {
|
const groupExists = async (req, res, next) => {
|
||||||
try {
|
let groupId = Number(req.params.groupId || req.body.groupId);
|
||||||
let groupId = req.params.groupId || req.body.groupId;
|
|
||||||
|
|
||||||
let group = await GroupService.getById(groupId);
|
if (isNaN(groupId)) throw new customError(`groupId is not a number`, responseCodes.responses.groups.id_not_found)
|
||||||
|
|
||||||
if (!group || group == statuses.not_found) return res.status(404).send("Group not found");
|
let group = await GroupService.getById(groupId);
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupExists: ${e}`)) }
|
if (!group) throw new customError(`group does not exists`, responseCodes.responses.groups.id_not_found);
|
||||||
|
next();
|
||||||
};
|
};
|
||||||
|
|
||||||
const groupDoesntExist = async (req, res, next) => {
|
const groupNameExists = async (req, res, next) => {
|
||||||
try {
|
let groupName = req.params.groupName || req.body.groupName;
|
||||||
let groupId = req.params.groupId || req.body.groupId;
|
|
||||||
|
|
||||||
let group = await GroupService.getById(groupId);
|
let group = await GroupService.getByName(groupName);
|
||||||
|
|
||||||
if (group || group != statuses.not_found) return res.status(400).send("Such group already exists");
|
if (!group) throw new customError(`group does not exists`, responseCodes.responses.groups.name_not_found);
|
||||||
next();
|
next();
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupDoesntExist: ${e}`)) }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const groupNameDoesntExist = async (req, res, next) => {
|
const localIdExists = async (req, res, next) => {
|
||||||
try {
|
let localId = req.params.localId || req.body.localId;
|
||||||
const { groupName } = req.params;
|
|
||||||
|
|
||||||
let group = await GroupService.getByName(groupName);
|
if (!localId) throw new customError(`local id is not specified`, responseCodes.responses.general.invalid_syntax);
|
||||||
if (group) return res.status(400).send("Such group name already exists");
|
next();
|
||||||
next();
|
}
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupNameDoesntExist: ${e}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const abstractProductExists = async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const { groupId, localId } = req.body;
|
|
||||||
|
|
||||||
let result = await AbstractProductService.exists(groupId, localId);
|
export default { groupExists, localIdExists, groupNameExists }
|
||||||
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}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const categoryNameDoesntExist = async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const { categoryName, localId, groupId } = req.body;
|
|
||||||
|
|
||||||
let result = await CategoryService.getByName(groupId, localId, categoryName);
|
|
||||||
if (result != statuses.not_found) return res.status(400).send("Such category name exists");
|
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/categoryNameDoesntExist: ${e}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
const categoryExists = async (req, res, next) => {
|
|
||||||
try {
|
|
||||||
const { localId, groupId } = req.body;
|
|
||||||
|
|
||||||
let result = await CategoryService.getById(groupId, localId);
|
|
||||||
if (!result || result == statuses.not_found) return res.status(404).send("No such category");
|
|
||||||
next();
|
|
||||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/categoryExists: ${e}`)); }
|
|
||||||
};
|
|
||||||
|
|
||||||
export default {
|
|
||||||
usernameExists,
|
|
||||||
usernameDoesntExist,
|
|
||||||
|
|
||||||
groupExists,
|
|
||||||
groupDoesntExist,
|
|
||||||
groupNameDoesntExist,
|
|
||||||
|
|
||||||
abstractProductExists,
|
|
||||||
productExists,
|
|
||||||
|
|
||||||
categoryNameDoesntExist,
|
|
||||||
categoryExists
|
|
||||||
};
|
|
||||||
12
src/response/customError.js
Normal file
12
src/response/customError.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
class customError extends Error {
|
||||||
|
constructor(message, code) {
|
||||||
|
super(message)
|
||||||
|
if (Error.captureStackTrace) {
|
||||||
|
Error.captureStackTrace(this, customError);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.code = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default customError;
|
||||||
26
src/response/errorHandler.js
Normal file
26
src/response/errorHandler.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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) => {
|
||||||
|
log.error(err);
|
||||||
|
console.log(err);
|
||||||
|
let language = req.headers["accept-language"];
|
||||||
|
|
||||||
|
if (err instanceof customError) {
|
||||||
|
return res.status(responses.getHTTPCode(err.code)).send(translate(language, err.code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const tryHandler = (func) => {
|
||||||
|
return async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
await func(req, res, next);
|
||||||
|
} catch (err) {
|
||||||
|
errorHandler(err, req, res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default tryHandler;
|
||||||
213
src/response/responseCodes.js
Normal file
213
src/response/responseCodes.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
const responses = {
|
||||||
|
authentication: {
|
||||||
|
not_found: "authentication not found",
|
||||||
|
invalid: "authentication invalid"
|
||||||
|
},
|
||||||
|
user: {
|
||||||
|
duplicate: "user duplicate",
|
||||||
|
invalid_syntax: "user invalid syntax",
|
||||||
|
already_in_group: "user already in group",
|
||||||
|
not_found: "user not found",
|
||||||
|
admin_leave: "admin leave"
|
||||||
|
},
|
||||||
|
usernames: {
|
||||||
|
duplicate: "username taken",
|
||||||
|
not_found: "username not found",
|
||||||
|
required: "username required"
|
||||||
|
},
|
||||||
|
passwords: {
|
||||||
|
required: "password required",
|
||||||
|
invalid: "password invalid"
|
||||||
|
},
|
||||||
|
groups: {
|
||||||
|
duplicate: "group name taken",
|
||||||
|
name_not_found: "group name not found",
|
||||||
|
id_not_found: "group id not found",
|
||||||
|
not_an_owner: "group not an owner",
|
||||||
|
not_a_member: "group not a member",
|
||||||
|
name_not_specified: "name not specified",
|
||||||
|
new_owner_not_specified: "new owner not specified"
|
||||||
|
},
|
||||||
|
abstractproducts: {
|
||||||
|
not_found: "abstract product not found",
|
||||||
|
invalid_syntax: "abstract product invalid syntax"
|
||||||
|
},
|
||||||
|
barcode: {
|
||||||
|
not_found: "barcode not found",
|
||||||
|
duplicate: "barcode duplicate",
|
||||||
|
too_long: "barcode too long",
|
||||||
|
invalid_syntax: "barcode invalid syntax"
|
||||||
|
},
|
||||||
|
abstractproductname: {
|
||||||
|
not_found: "abstract product name not found",
|
||||||
|
duplicate: "abstract product name duplicate",
|
||||||
|
too_long: "abstract product name too long",
|
||||||
|
invalid_syntax: "abstract product name invalid syntax"
|
||||||
|
},
|
||||||
|
netweight: {
|
||||||
|
too_long: "net weight too long",
|
||||||
|
invalid_syntax: "net weight invalid syntax"
|
||||||
|
},
|
||||||
|
imagehash: {
|
||||||
|
too_long: "image hash too long",
|
||||||
|
invalid_syntax: "image hash invalid syntax"
|
||||||
|
},
|
||||||
|
category: {
|
||||||
|
too_long: "category too long",
|
||||||
|
invalid_syntax: "category invalid syntax",
|
||||||
|
not_found: "category not found"
|
||||||
|
},
|
||||||
|
unit: {
|
||||||
|
too_long: "unit too long",
|
||||||
|
invalid_syntax: "unit invalid syntax"
|
||||||
|
},
|
||||||
|
products: {
|
||||||
|
not_found: "product not found",
|
||||||
|
invalid_syntax: "product invalid syntax"
|
||||||
|
},
|
||||||
|
abstractproductid: {
|
||||||
|
not_found: "abstract product id not found",
|
||||||
|
invalid_syntax: "abstract product id invalid syntax"
|
||||||
|
},
|
||||||
|
amount: {
|
||||||
|
too_long: "amount too long",
|
||||||
|
invalid_syntax: "amount invalid syntax"
|
||||||
|
},
|
||||||
|
dateofproduction: {
|
||||||
|
too_long: "date of production too long",
|
||||||
|
invalid_syntax: "date of production invalid syntax"
|
||||||
|
},
|
||||||
|
expirydate: {
|
||||||
|
too_long: "expiry date too long",
|
||||||
|
invalid_syntax: "expiry date invalid syntax"
|
||||||
|
},
|
||||||
|
categories: {
|
||||||
|
duplicate: "categories duplicate",
|
||||||
|
not_found: "categories not found"
|
||||||
|
},
|
||||||
|
general: {
|
||||||
|
ok: "ok",
|
||||||
|
invalid_syntax: "invalid syntax",
|
||||||
|
png_only: "png only",
|
||||||
|
too_long: "too long",
|
||||||
|
unknown: "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getHTTPCode = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case responses.general.ok:
|
||||||
|
return 200
|
||||||
|
case responses.general.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.general.unknown:
|
||||||
|
return 500
|
||||||
|
case responses.general.png_only:
|
||||||
|
return 400
|
||||||
|
case responses.general.too_long:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.authentication.invalid:
|
||||||
|
return 403
|
||||||
|
case responses.authentication.not_found:
|
||||||
|
return 403
|
||||||
|
|
||||||
|
case responses.user.already_in_group:
|
||||||
|
return 409
|
||||||
|
case responses.user.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.user.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.user.not_found:
|
||||||
|
return 404
|
||||||
|
case responses.user.admin_leave:
|
||||||
|
return 409
|
||||||
|
|
||||||
|
|
||||||
|
case responses.usernames.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.usernames.not_found:
|
||||||
|
return 404
|
||||||
|
case responses.usernames.required:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.passwords.required:
|
||||||
|
return 400
|
||||||
|
case responses.passwords.invalid:
|
||||||
|
return 403
|
||||||
|
|
||||||
|
case responses.groups.id_not_found:
|
||||||
|
return 404
|
||||||
|
case responses.groups.name_not_found:
|
||||||
|
return 404
|
||||||
|
case responses.groups.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.groups.not_an_owner:
|
||||||
|
return 403
|
||||||
|
case responses.groups.not_a_member:
|
||||||
|
return 403
|
||||||
|
case responses.groups.name_not_specified:
|
||||||
|
return 400
|
||||||
|
case responses.new_owner_not_specified:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.abstractproducts.not_found:
|
||||||
|
return 404
|
||||||
|
case responses.abstractproducts.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.abstractproductid.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.abstractproductid.not_found:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
case responses.abstractproductname.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.abstractproductname.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.abstractproductname.not_found:
|
||||||
|
return 404
|
||||||
|
case responses.abstractproductname.too_long:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.amount.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.amount.too_long:
|
||||||
|
return 409
|
||||||
|
|
||||||
|
case responses.barcode.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.barcode.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.barcode.not_found:
|
||||||
|
return 404
|
||||||
|
case responses.barcode.too_long:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.categories.duplicate:
|
||||||
|
return 409
|
||||||
|
case responses.categories.not_found:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
case responses.category.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.category.too_long:
|
||||||
|
return 400
|
||||||
|
case responses.category.not_found:
|
||||||
|
return 404
|
||||||
|
|
||||||
|
case responses.dateofproduction.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.dateofproduction.too_long:
|
||||||
|
return 400
|
||||||
|
|
||||||
|
case responses.expirydate.invalid_syntax:
|
||||||
|
return 400
|
||||||
|
case responses.expirydate.too_long:
|
||||||
|
return 400
|
||||||
|
default:
|
||||||
|
return 500
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { responses, getHTTPCode }
|
||||||
@@ -1,11 +1,21 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import auth from '../middlewares/auth.js';
|
import auth from '../middlewares/auth.js';
|
||||||
import AbstractProductController from '../controllers/abstractproduct.js';
|
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';
|
import existance from '../middlewares/existance.js';
|
||||||
|
|
||||||
|
const upload = multer(({
|
||||||
|
dest: path.join(path.resolve(path.dirname('')), "/temp")
|
||||||
|
}));
|
||||||
|
|
||||||
const AbstractProductRouter = new Router();
|
const AbstractProductRouter = new Router();
|
||||||
|
|
||||||
AbstractProductRouter.post('/create', auth.authenticate, existance.groupExists, auth.userIsInGroup, AbstractProductController.create);
|
AbstractProductRouter.post('/create', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(AbstractProductController.create));
|
||||||
AbstractProductRouter.post('/update', auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.abstractProductExists, AbstractProductController.update);
|
AbstractProductRouter.post('/update', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(AbstractProductController.update));
|
||||||
|
AbstractProductRouter.get('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(AbstractProductController.getById));
|
||||||
|
AbstractProductRouter.get('/getImage/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(AbstractProductController.getImage));
|
||||||
|
AbstractProductRouter.delete('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(AbstractProductController.delete));
|
||||||
|
|
||||||
export default AbstractProductRouter;
|
export default AbstractProductRouter;
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import auth from '../middlewares/auth.js';
|
import auth from '../middlewares/auth.js';
|
||||||
import existance from '../middlewares/existance.js';
|
|
||||||
import CategoryController from '../controllers/category.js';
|
import CategoryController from '../controllers/category.js';
|
||||||
|
import tryHandler from '../response/errorHandler.js';
|
||||||
|
import existance from '../middlewares/existance.js';
|
||||||
|
|
||||||
const CategoryRouter = new Router();
|
const CategoryRouter = new Router();
|
||||||
|
|
||||||
CategoryRouter.post('/create', auth.authenticate, existance.groupExists, existance.categoryNameDoesntExist, CategoryController.create);
|
CategoryRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(CategoryController.create));
|
||||||
CategoryRouter.post('/update', auth.authenticate, existance.groupExists, existance.categoryExists, CategoryController.update);
|
CategoryRouter.post('/update', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(CategoryController.update));
|
||||||
|
CategoryRouter.get('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(CategoryController.getByLocalId));
|
||||||
|
CategoryRouter.delete('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(existance.localIdExists), tryHandler(CategoryController.delete));
|
||||||
|
|
||||||
export default CategoryRouter;
|
export default CategoryRouter;
|
||||||
@@ -1,12 +1,19 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import auth from '../middlewares/auth.js';
|
import auth from '../middlewares/auth.js';
|
||||||
import GroupController from '../controllers/group.js';
|
import GroupController from '../controllers/group.js';
|
||||||
|
import tryHandler from '../response/errorHandler.js';
|
||||||
import existance from '../middlewares/existance.js';
|
import existance from '../middlewares/existance.js';
|
||||||
|
|
||||||
const GroupRouter = new Router();
|
const GroupRouter = new Router();
|
||||||
|
|
||||||
GroupRouter.post('/create/:groupName', auth.authenticate, existance.groupNameDoesntExist, GroupController.create);
|
GroupRouter.post('/create/:groupName', tryHandler(auth.authenticate), tryHandler(GroupController.create));
|
||||||
GroupRouter.post('/join/:groupId', auth.authenticate, existance.groupExists, auth.requirePassword, auth.checkGroupPassword, GroupController.join);
|
GroupRouter.post('/join/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.requirePassword), tryHandler(auth.checkGroupPassword), tryHandler(GroupController.join));
|
||||||
GroupRouter.post('/password/:groupId', auth.authenticate, existance.groupExists, auth.authorizeGroupOwner, auth.requirePassword, GroupController.updatePassword);
|
GroupRouter.get('/leave/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(GroupController.leave));
|
||||||
|
GroupRouter.post('/password/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.authorizeGroupOwner), tryHandler(auth.requirePassword), tryHandler(GroupController.updatePassword));
|
||||||
|
GroupRouter.get('/byName/:groupName', tryHandler(auth.authenticate), tryHandler(existance.groupNameExists), tryHandler(GroupController.getByName));
|
||||||
|
GroupRouter.get('/byId/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(GroupController.getById));
|
||||||
|
GroupRouter.get('/getUsers/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(GroupController.getUsersInGroup));
|
||||||
|
GroupRouter.get('/adminId/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(GroupController.getAdminId));
|
||||||
|
GroupRouter.post('/rename/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.authorizeGroupOwner), tryHandler(GroupController.rename));
|
||||||
|
GroupRouter.post('/transferOwnership/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.authorizeGroupOwner), tryHandler(GroupController.transferOwnership));
|
||||||
export default GroupRouter;
|
export default GroupRouter;
|
||||||
@@ -1,11 +1,14 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import auth from '../middlewares/auth.js';
|
import auth from '../middlewares/auth.js';
|
||||||
import ProductController from '../controllers/product.js'
|
import ProductController from '../controllers/product.js'
|
||||||
|
import tryHandler from '../response/errorHandler.js';
|
||||||
import existance from '../middlewares/existance.js';
|
import existance from '../middlewares/existance.js';
|
||||||
|
|
||||||
const ProductRouter = new Router();
|
const ProductRouter = new Router();
|
||||||
|
|
||||||
ProductRouter.post('/create', auth.authenticate, existance.groupExists, auth.userIsInGroup, ProductController.create);
|
ProductRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.create));
|
||||||
ProductRouter.post('/update', auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.productExists, ProductController.update);
|
ProductRouter.post('/update', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.update));
|
||||||
|
ProductRouter.get('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.getByLocalId))
|
||||||
|
ProductRouter.delete('/:groupId/:localId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.delete))
|
||||||
|
|
||||||
export default ProductRouter;
|
export default ProductRouter;
|
||||||
@@ -1,12 +1,17 @@
|
|||||||
import { Router } from 'express';
|
import { Router } from 'express';
|
||||||
import auth from '../middlewares/auth.js';
|
import auth from '../middlewares/auth.js';
|
||||||
import existance from '../middlewares/existance.js';
|
|
||||||
import UserController from '../controllers/user.js';
|
import UserController from '../controllers/user.js';
|
||||||
|
import tryHandler from '../response/errorHandler.js';
|
||||||
|
|
||||||
const UserRouter = new Router();
|
const UserRouter = new Router();
|
||||||
|
|
||||||
UserRouter.post('/register', auth.requireUsername, auth.requirePassword, existance.usernameDoesntExist, UserController.register);
|
UserRouter.post('/register', tryHandler(auth.requireUsername), tryHandler(auth.requirePassword), tryHandler(UserController.register));
|
||||||
UserRouter.post('/login', auth.requireUsername, auth.requirePassword, existance.usernameExists, UserController.login);
|
UserRouter.post('/login', tryHandler(auth.requireUsername), tryHandler(auth.requirePassword), tryHandler(UserController.login));
|
||||||
UserRouter.get('/synchronize/:groupId', auth.authenticate, existance.groupExists, auth.userIsInGroup, UserController.synchronize);
|
UserRouter.get('/synchronize/:groupId', tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(UserController.synchronize));
|
||||||
|
UserRouter.post('/changeUsername', tryHandler(auth.authenticate), tryHandler(auth.requireUsername), tryHandler(UserController.changeUsername));
|
||||||
|
UserRouter.post('/changePassword', tryHandler(auth.authenticate), tryHandler(auth.requirePassword), tryHandler(UserController.changePassword));
|
||||||
|
UserRouter.get('/myGroups', tryHandler(auth.authenticate), tryHandler(UserController.getAllGroupsForUser));
|
||||||
|
//Maybe I should add a middleware like "auth.usersShareGroups"
|
||||||
|
UserRouter.get('/byId/:userId', tryHandler(auth.authenticate), tryHandler(UserController.getById))
|
||||||
|
|
||||||
export default UserRouter;
|
export default UserRouter;
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
|
import ProductService from './product.js';
|
||||||
import db from '../db.js';
|
import db from '../db.js';
|
||||||
import statuses from '../utils/status.js';
|
|
||||||
import errorHandler from '../utils/pgerrorhandler.js';
|
import errorHandler from '../utils/pgerrorhandler.js';
|
||||||
|
import responses from '../response/responseCodes.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
|
||||||
class AbstractProductService {
|
class AbstractProductService {
|
||||||
async create(groupid, localid, 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])
|
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) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "abstract product")
|
errorHandler(e, "abstractproduct")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,21 +22,21 @@ class AbstractProductService {
|
|||||||
async updateName(groupId, localId, name) {
|
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])
|
await db.query("UPDATE abstract_products SET name = $1 WHERE group_id = $2 AND local_id = $3", [name, groupId, localId])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "name")
|
errorHandler(e, "abstractproductname")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateNetWeight(groupId, localId, net_weight) {
|
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]
|
await db.query("UPDATE abstract_products SET net_weight = $1 WHERE group_id = $2 AND local_id = $3", [net_weight, groupId, localId])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "net weight")
|
errorHandler(e, "netweight")
|
||||||
}));
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateImageFilename(groupId, localId, image_filename) {
|
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])
|
await db.query("UPDATE abstract_products SET image_filename = $1 WHERE group_id = $2 AND local_id = $3", [image_filename, groupId, localId])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "image filename")
|
errorHandler(e, "imagehash")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +56,7 @@ class AbstractProductService {
|
|||||||
|
|
||||||
async getAll(groupId) {
|
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;
|
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;
|
// if (!result) throw new customError("getAll abstract products not found", responses.responses.abstractproducts.not_found)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,6 +65,25 @@ class AbstractProductService {
|
|||||||
if (!result) return false;
|
if (!result) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getByLocalId(groupId, localId) {
|
||||||
|
let result = (await db.query("SELECT * FROM abstract_products WHERE group_id = $1 AND local_id = $2", [groupId, localId]))
|
||||||
|
if (result.rowCount == 0) throw new customError(`Abstract product not found`, responses.responses.abstractproducts.not_found)
|
||||||
|
return result.rows[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllFromCategory(groupId, categoryId) {
|
||||||
|
let result = (await db.query("SELECT * FROM abstract_products WHERE group_id = $1 AND category = $2", [groupId, categoryId]));
|
||||||
|
return result.rows
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(groupId, localId) {
|
||||||
|
let productsOfAbstractProduct = await ProductService.getByAbstractProductID(groupId, localId);
|
||||||
|
productsOfAbstractProduct.forEach(async (product) => {
|
||||||
|
await ProductService.delete(product.id)
|
||||||
|
});
|
||||||
|
await db.query("DELETE FROM abstract_products WHERE group_id = $1 AND local_id = $2", [groupId, localId])
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default new AbstractProductService();
|
export default new AbstractProductService();
|
||||||
@@ -1,5 +1,9 @@
|
|||||||
|
import ProductService from "./product.js";
|
||||||
|
|
||||||
import db from '../db.js';
|
import db from '../db.js';
|
||||||
import statuses from '../utils/status.js';
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
import AbstractProductService from "./abstractproduct.js";
|
||||||
|
|
||||||
class CategoryService {
|
class CategoryService {
|
||||||
async create(groupId, categoryId, name) {
|
async create(groupId, categoryId, name) {
|
||||||
@@ -11,16 +15,31 @@ class CategoryService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getById(groupId, localId) {
|
async getById(groupId, localId) {
|
||||||
let result = (await db.query("SELECT * FROM categories WHERE group_id = $1 AND local_id = $2", [groupId, localId]))
|
let result = (await db.query("SELECT * FROM categories WHERE group_id = $1 AND local_id = $2", [groupId, localId]));
|
||||||
if (result.rowCount == 0) return statuses.not_found;
|
if (result.rowCount == 0) throw new customError(`getById categorirs not found`, responseCodes.responses.category.not_found)
|
||||||
return result.rows[0];
|
return result.rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByName(groupId, localId, name) {
|
async getByName(groupId, localId, name) {
|
||||||
let result = (await db.query("SELECT * FROM categories WHERE group_id = $1 AND local_id = $2 AND name = $3", [groupId, localId, name]));
|
let result = (await db.query("SELECT * FROM categories WHERE group_id = $1 AND local_id = $2 AND name = $3", [groupId, localId, name]));
|
||||||
if (result.rowCount == 0) return statuses.not_found;
|
if (result.rowCount == 0) throw new customError(`getByName categories not found`, responseCodes.responses.category.not_found)
|
||||||
return result.rows[0];
|
return result.rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getAll(groupId) {
|
||||||
|
let result = (await db.query("SELECT group_id, local_id, name FROM categories WHERE group_id = $1", [groupId])).rows
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async delete(groupId, localId) {
|
||||||
|
let abstractProductInCategory = await AbstractProductService.getAllFromCategory(groupId, localId);
|
||||||
|
abstractProductInCategory.forEach(async (abstractProduct) => {
|
||||||
|
await AbstractProductService.delete(groupId, abstractProduct.local_id)
|
||||||
|
});
|
||||||
|
|
||||||
|
await db.query("DELETE FROM categories WHERE group_id = $1 AND local_id = $2", [groupId, localId]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new CategoryService();
|
export default new CategoryService();
|
||||||
@@ -1,17 +1,18 @@
|
|||||||
import db from '../db.js';
|
import db from '../db.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
import errorHandler from '../utils/pgerrorhandler.js';
|
import errorHandler from '../utils/pgerrorhandler.js';
|
||||||
import status from '../utils/status.js';
|
|
||||||
|
|
||||||
class GroupService {
|
class GroupService {
|
||||||
async create(name, creatorId) {
|
async create(name, creatorId) {
|
||||||
let res = await db.query("INSERT INTO groups (name, admin_id) VALUES ($1, $2) RETURNING ID", [name, creatorId]).catch(errorHandler);
|
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];
|
return res.rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getById(id) {
|
async getById(id) {
|
||||||
let res = (await db.query("SELECT * FROM groups WHERE id = $1", [id]));
|
let res = (await db.query("SELECT * FROM groups WHERE id = $1", [id]));
|
||||||
if (res.rowCount == 0) return status.not_found;
|
if (res.rowCount == 0) throw new customError(`getById group not found`, responseCodes.responses.groups.id_not_found);
|
||||||
return res.rows[0];
|
return res.rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +29,21 @@ class GroupService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async getByName(name) {
|
async getByName(name) {
|
||||||
return (await db.query("SELECT * FROM groups WHERE name = $1", [name])).rows[0];
|
let res = (await db.query("SELECT id FROM groups WHERE name = $1", [name]));
|
||||||
|
if (res.rowCount == 0) throw new customError(`getByName group not found`, responseCodes.responses.groups.name_not_found);
|
||||||
|
return res.rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getUsersInGroup(groupId) {
|
||||||
|
return (await db.query("SELECT id FROM users WHERE $1 = ANY(groups)", [groupId])).rows.map((group) => group.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
async rename(groupId, newName) {
|
||||||
|
await db.query("UPDATE groups SET name = $1 WHERE id = $2", [newName, groupId]).catch((e) => errorHandler(e, "groups"));;
|
||||||
|
}
|
||||||
|
|
||||||
|
async transferOwnership(groupId, userId) {
|
||||||
|
await db.query("UPDATE groups SET admin_id = $1 WHERE id = $2", [userId, groupId])
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import db from '../db.js';
|
import db from '../db.js';
|
||||||
import statuses from '../utils/status.js';
|
|
||||||
import errorHandler from '../utils/pgerrorhandler.js';
|
import errorHandler from '../utils/pgerrorhandler.js';
|
||||||
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
|
|
||||||
class ProductService {
|
class ProductService {
|
||||||
async create(groupid, localid, abstract_product_id, amount, date_of_production, expiry_date) {
|
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])
|
await db.query("INSERT INTO products (group_id, local_id, abstract_product_id, amount, date_of_production, expiry_date) VALUES ($1, $2, $3, $4, to_timestamp($5)::date, to_timestamp($6)::date)", [groupid, localid, abstract_product_id, amount, date_of_production, expiry_date])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "Abstract Product")
|
errorHandler(e, "product")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateAbstractProductId(groupId, localId, abstract_product_id) {
|
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])
|
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) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "abstract product id")
|
errorHandler(e, "abstractproductid")
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -26,22 +27,22 @@ class ProductService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async updateDateOfProduction(groupId, localId, date_of_production) {
|
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])
|
await db.query("UPDATE products SET date_of_production = to_timestamp($1)::date WHERE group_id = $2 AND local_id = $3", [date_of_production, groupId, localId])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "date of production")
|
errorHandler(e, "dateofproduction")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateExpiryDate(groupId, localId, expiry_date) {
|
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])
|
await db.query("UPDATE products SET expiry_date = to_timestamp($1)::date WHERE group_id = $2 AND local_id = $3", [expiry_date, groupId, localId])
|
||||||
.catch((e) => {
|
.catch((e) => {
|
||||||
errorHandler(e, "expiry date")
|
errorHandler(e, "expirydate")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll(groupId) {
|
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;
|
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;
|
// if (!result) throw new customError(`getAll product not found`, responseCodes.responses.products.not_found);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,6 +51,21 @@ class ProductService {
|
|||||||
if (!result) return false;
|
if (!result) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getByLocalId(groupId, localId) {
|
||||||
|
let result = (await db.query("SELECT * FROM products WHERE group_id = $1 AND local_id = $2", [groupId, localId]));
|
||||||
|
if (result.rowCount == 0) throw new customError(`getByLocalId product not found`, responseCodes.responses.products.not_found);
|
||||||
|
return result.rows[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByAbstractProductID(groupId, abstractProductID) {
|
||||||
|
let result = (await db.query("SELECT * FROM products WHERE group_id = $1 AND abstract_product_id = $2", [groupId, abstractProductID]))
|
||||||
|
return result.rows
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(ID) {
|
||||||
|
await db.query("DELETE FROM products WHERE id = $1", [ID])
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export default new ProductService();
|
export default new ProductService();
|
||||||
|
|||||||
@@ -1,23 +1,28 @@
|
|||||||
import db from '../db.js'
|
import db from '../db.js'
|
||||||
import statuses from '../utils/status.js';
|
import customError from '../response/customError.js';
|
||||||
|
import responseCodes from '../response/responseCodes.js';
|
||||||
import bcrypt from 'bcrypt';
|
import bcrypt from 'bcrypt';
|
||||||
|
import errorHandler from '../utils/pgerrorhandler.js';
|
||||||
|
|
||||||
class UserService {
|
class UserService {
|
||||||
async create(username, password) {
|
async create(username, password) {
|
||||||
await db.query("INSERT INTO users (username, password) VALUES ($1, $2)", [username, bcrypt.hashSync(password, 12)]).catch((e) => {
|
let result = (await db.query("INSERT INTO users (username, password) VALUES ($1, $2) RETURNING id", [username, bcrypt.hashSync(password, 12)]).catch((e) => {
|
||||||
errorHandler(e, "user");
|
errorHandler(e, "user");
|
||||||
})
|
})).rows[0].id
|
||||||
return statuses.ok;
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
async getByUsername(username) {
|
async getByUsername(username) {
|
||||||
let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username])).rows;
|
let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username]));
|
||||||
if (user == undefined) return statuses.not_found;
|
if (user.rowCount == 0) throw new customError(`getByUsername user not found`, responseCodes.responses.usernames.not_found);
|
||||||
return (user[0]);
|
return user.rows[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAll() {
|
async getAll() {
|
||||||
return (await db.query("SELECT * FROM Users")).rows;
|
let res = await db.query("SELECT * FROM Users");
|
||||||
|
if (res.rowCount == 0) throw new customError(`getAll user not found`, responseCodes.responses.usernames.not_found);
|
||||||
|
return res.rows[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
async isInGroup(userId, groupId) {
|
async isInGroup(userId, groupId) {
|
||||||
@@ -25,9 +30,30 @@ class UserService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async joinGroup(userId, groupId) {
|
async joinGroup(userId, groupId) {
|
||||||
if (await (this.isInGroup(userId, groupId))) return statuses.duplicate;
|
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]);
|
await db.query("UPDATE users SET groups = array_append(groups, $1::integer) WHERE ID = $2", [groupId, userId]);
|
||||||
return statuses.ok;
|
}
|
||||||
|
|
||||||
|
async leaveGroup(userId, groupId) {
|
||||||
|
await db.query("UPDATE users SET groups = array_remove(groups, $1::integer) WHERE ID = $2", [groupId, userId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
async changeUsername(userId, username) {
|
||||||
|
await db.query("UPDATE users SET username = $1 WHERE id = $2", [username, userId]).catch(e => errorHandler(e, "user"));
|
||||||
|
}
|
||||||
|
|
||||||
|
async changePassword(userId, password) {
|
||||||
|
await db.query("UPDATE users SET password = $1 WHERE id = $2", [bcrypt.hashSync(password, 12), userId])
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAllGroupsForUser(userId) {
|
||||||
|
return (await db.query("SELECT groups FROM users WHERE id = $1", [userId])).rows[0].groups
|
||||||
|
}
|
||||||
|
|
||||||
|
async getById(userId) {
|
||||||
|
let res = (await db.query("SELECT * FROM users WHERE id = $1", [userId]))
|
||||||
|
if (res.rowCount == 0) throw new customError(`getById no user found`, responseCodes.responses.user.not_found)
|
||||||
|
return res.rows[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,8 @@ const genToken = (login) => {
|
|||||||
return jwt.sign(payload, config.secret, { expiresIn: "7d" });
|
return jwt.sign(payload, config.secret, { expiresIn: "7d" });
|
||||||
};
|
};
|
||||||
|
|
||||||
export default genToken;
|
const getUserIdFromToken = (token) => {
|
||||||
|
return jwt.decode(token).login.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default { genToken, getUserIdFromToken };
|
||||||
@@ -2,23 +2,23 @@ import config from '../../config.json' with {type: "json"};
|
|||||||
|
|
||||||
const debug = (text) => {
|
const debug = (text) => {
|
||||||
if (config.debug) console.debug(`[D] [${Date()}]: ${text}`);
|
if (config.debug) console.debug(`[D] [${Date()}]: ${text}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
const info = (text) => {
|
const info = (text) => {
|
||||||
console.log(`[I] [${Date()}]: ${text}`);
|
console.log(`[I] [${Date()}]: ${text}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
const error = (text) => {
|
const error = (text) => {
|
||||||
console.error(`[E] [${Date()}]: ${text}`);
|
console.error(`[E] [${Date()}]: ${text}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
const warn = (text) => {
|
const warn = (text) => {
|
||||||
console.warn(`[W] [${Date()}]: ${text}`);
|
console.warn(`[W] [${Date()}]: ${text}`);
|
||||||
}
|
};
|
||||||
|
|
||||||
const unknownError = (text) => {
|
const unknownError = (text) => {
|
||||||
error(text);
|
error(text);
|
||||||
return "Unknown server error. Please, report to the developer";
|
return "Unknown server error. Please, report to the developer";
|
||||||
}
|
};
|
||||||
|
|
||||||
export default { debug, info, error, warn, unknownError };
|
export default { debug, info, error, warn, unknownError };
|
||||||
28
src/utils/notify.js
Normal file
28
src/utils/notify.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import clients from '../index.js';
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
import config from '../../config.json' with {type: "json"};
|
||||||
|
import log from './log.js';
|
||||||
|
|
||||||
|
const notify = (token, groupId, action, item, data) => {
|
||||||
|
let login = jwt.decode(token, config.secret).login
|
||||||
|
let userIdCurrent = login.id
|
||||||
|
|
||||||
|
let payload = {
|
||||||
|
action,
|
||||||
|
item,
|
||||||
|
groupId,
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
clients.forEach(client => {
|
||||||
|
if (client.currentGroup == groupId) {
|
||||||
|
let userIdFromToken = jwt.decode(client.token, config.secret).login.id
|
||||||
|
if (userIdCurrent == userIdFromToken) return;
|
||||||
|
log.debug(`Sending user with ID ${userIdCurrent} packet "${JSON.stringify(payload)}"`)
|
||||||
|
client.socket.send(JSON.stringify(payload))
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default notify;
|
||||||
@@ -1,29 +1,18 @@
|
|||||||
import statuses from "./status.js";
|
import customError from "../response/customError.js";
|
||||||
|
import responseCodes from "../response/responseCodes.js";
|
||||||
|
|
||||||
const errorHandler = (e, obj) => {
|
const errorHandler = (e, obj) => {
|
||||||
switch (e.code) {
|
switch (e.code) {
|
||||||
case '23505':
|
case '23505':
|
||||||
throw {
|
throw new customError(`Duplicate ${obj}`, responseCodes.responses[obj].duplicate)
|
||||||
status: statuses.duplicate,
|
|
||||||
message: `Such ${obj} already exists`
|
|
||||||
};
|
|
||||||
case '22007':
|
case '22007':
|
||||||
throw {
|
throw new customError(`Invalid syntax ${obj}`, responseCodes.responses.general.invalid_syntax)
|
||||||
status: statuses.invalid_syntax,
|
|
||||||
message: `Invalid syntax in ${obj}`
|
|
||||||
};
|
|
||||||
case '22001':
|
case '22001':
|
||||||
throw {
|
throw new customError(`Value too long ${obj}`, responseCodes.responses[obj].too_long)
|
||||||
status: statuses.invalid_syntax,
|
|
||||||
message: `Value too long (${obj})`
|
|
||||||
};
|
|
||||||
default:
|
default:
|
||||||
throw {
|
console.log(e)
|
||||||
status: statuses.unknown,
|
throw new customError(`Unknown error ${obj}`, responseCodes.responses.general.unknown)
|
||||||
message: `Unknown error. Please, report to the developer`,
|
|
||||||
original: e
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default errorHandler;
|
export default errorHandler;
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
const statuses = {
|
|
||||||
ok: "ok",
|
|
||||||
duplicate: "duplicate",
|
|
||||||
not_found: "not found",
|
|
||||||
invalid_syntax: "invalid syntax",
|
|
||||||
unknown: "unknown"
|
|
||||||
}
|
|
||||||
|
|
||||||
export default statuses
|
|
||||||
9
src/utils/translate.js
Normal file
9
src/utils/translate.js
Normal file
@@ -0,0 +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];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate
|
||||||
Reference in New Issue
Block a user