Compare commits
47 Commits
0e9a7f0856
...
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 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -139,3 +139,4 @@ dist
|
||||
#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
|
||||
|
||||
## 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": "Неизвестная ошибка сервера! Пожалуйста, сообщите разработчику!"
|
||||
}
|
||||
217
package-lock.json
generated
217
package-lock.json
generated
@@ -15,7 +15,9 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nodemon": "^3.1.7",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.13.1"
|
||||
"pg": "^8.13.1",
|
||||
"websocket": "^1.0.35",
|
||||
"ws": "^8.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@mapbox/node-pre-gyp": {
|
||||
@@ -228,6 +230,19 @@
|
||||
"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",
|
||||
@@ -408,6 +423,19 @@
|
||||
"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": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
|
||||
@@ -527,12 +555,67 @@
|
||||
"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": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"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": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
@@ -542,6 +625,16 @@
|
||||
"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": {
|
||||
"version": "4.21.1",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz",
|
||||
@@ -599,6 +692,15 @@
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
|
||||
"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": {
|
||||
"version": "7.1.1",
|
||||
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
|
||||
@@ -987,6 +1089,12 @@
|
||||
"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",
|
||||
@@ -1274,6 +1382,12 @@
|
||||
"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": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz",
|
||||
@@ -1300,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": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.7.tgz",
|
||||
@@ -1988,6 +2113,12 @@
|
||||
"integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==",
|
||||
"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": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
@@ -2007,6 +2138,15 @@
|
||||
"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": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
|
||||
@@ -2022,6 +2162,19 @@
|
||||
"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",
|
||||
@@ -2067,6 +2220,38 @@
|
||||
"integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==",
|
||||
"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": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
|
||||
@@ -2092,6 +2277,27 @@
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"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": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
@@ -2101,6 +2307,15 @@
|
||||
"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": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"nodemon": "^3.1.7",
|
||||
"path": "^0.12.7",
|
||||
"pg": "^8.13.1"
|
||||
"pg": "^8.13.1",
|
||||
"websocket": "^1.0.35",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"type": "module"
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
{
|
||||
"debug": true,
|
||||
"port": "8081",
|
||||
"wsport": "8282",
|
||||
"dbuser": "bsfe",
|
||||
"dbhost": "localhost",
|
||||
"dbport": "5432",
|
||||
@@ -1,79 +1,115 @@
|
||||
import AbstractProductService from '../services/abstractproduct.js';
|
||||
import statuses from '../utils/status.js';
|
||||
import log from '../utils/log.js';
|
||||
import ProductService from '../services/product.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";
|
||||
|
||||
class AbstractProductController {
|
||||
async create(req, res) {
|
||||
try {
|
||||
const { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body;
|
||||
const { groupId, localId, barcode, name, net_weight, category, unit } = req.body;
|
||||
|
||||
const tempPath = req.file.path;
|
||||
const targetPath = path.join(path.resolve(path.dirname('')), `/uploads/${image_filename}.png`);
|
||||
if (!req.file) throw new customError(`user hasn't supplied a file for abstract product creation`, responseCodes.responses.image_filename)
|
||||
|
||||
if (path.extname(req.file.originalname).toLowerCase() !== ".png") {
|
||||
return res.status(400).send("Only .png files are allowed")
|
||||
}
|
||||
const tempPath = req.file.path;
|
||||
const image_buffer = fs.readFileSync(tempPath);
|
||||
const image_filename = createHash('md5').update(image_buffer).digest('hex');
|
||||
const targetPath = path.join(path.resolve(path.dirname('')), `/uploads/${image_filename}.png`);
|
||||
|
||||
fs.renameSync(tempPath, targetPath);
|
||||
|
||||
await AbstractProductService.create(groupId, localId, barcode, name, net_weight, image_filename, category, unit);
|
||||
return res.status(200).send("Successfull");
|
||||
} catch (e) {
|
||||
switch (e.status) {
|
||||
case statuses.duplicate:
|
||||
return res.status(400).send(e.message);
|
||||
default:
|
||||
log.error(e.original);
|
||||
return res.status(500).send(e.message);
|
||||
}
|
||||
if (path.extname(req.file.originalname).toLowerCase() !== ".png") {
|
||||
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) {
|
||||
try {
|
||||
let { groupId, localId, barcode, name, net_weight, image_filename, category, unit } = req.body;
|
||||
let { groupId, localId, barcode, name, net_weight, category, unit } = req.body;
|
||||
|
||||
const tempPath = req.file.path;
|
||||
const targetPath = path.join(path.resolve(path.dirname('')) + `/uploads/${image_filename}.png`);
|
||||
var tempPath, image_buffer, image_filename, targetPath;
|
||||
|
||||
if (req.file) {
|
||||
tempPath = req.file.path;
|
||||
|
||||
let previousImageHash = (await AbstractProductService.getByLocalId(groupId, localId)).image_filename
|
||||
let previousImagePath = path.join(path.resolve(path.dirname('')), `/uploads/${previousImageHash}.png`);
|
||||
fs.unlinkSync(previousImagePath)
|
||||
|
||||
image_buffer = fs.readFileSync(tempPath);
|
||||
image_filename = createHash('md5').update(image_buffer).digest('hex');
|
||||
targetPath = path.join(path.resolve(path.dirname('')) + `/uploads/${image_filename}.png`);
|
||||
fs.copyFileSync(tempPath, targetPath);
|
||||
fs.rmSync(tempPath);
|
||||
|
||||
|
||||
if (barcode) await AbstractProductService.updateBarcode(groupId, localId, barcode);
|
||||
|
||||
if (name) await AbstractProductService.updateName(groupId, localId, name);
|
||||
|
||||
if (net_weight) await AbstractProductService.updateNetWeight(groupId, localId, net_weight);
|
||||
|
||||
if (image_filename && tempPath) {
|
||||
fs.renameSync(tempPath, targetPath);
|
||||
await AbstractProductService.updateImageFilename(groupId, localId, image_filename);
|
||||
} else if (image_filename && !tempPath) {
|
||||
return res.status(400).send("You must supply image file along with its hash");
|
||||
} else if (!image_file && tempPath) {
|
||||
return res.status(400).send("You must supply image file hash along with file");
|
||||
}
|
||||
|
||||
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 updateImage(req, res) {}
|
||||
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();
|
||||
@@ -1,25 +1,51 @@
|
||||
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";
|
||||
|
||||
class CategoryController {
|
||||
async create(req, res) {
|
||||
try {
|
||||
const { localId, categoryName, groupId } = req.body;
|
||||
const { localId, categoryName, groupId } = req.body;
|
||||
|
||||
await CategoryService.create(groupId, localId, categoryName);
|
||||
return res.status(200).send("Success");
|
||||
await CategoryService.create(groupId, localId, categoryName);
|
||||
|
||||
} 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) {
|
||||
try {
|
||||
const { localId, groupId, name } = req.body;
|
||||
const { localId, groupId, categoryName } = req.body;
|
||||
|
||||
await CategoryService.update(groupId, localId, name);
|
||||
return res.status(200).send("Success");
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/update: ${e}`)); }
|
||||
await CategoryService.update(groupId, localId, categoryName);
|
||||
|
||||
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,49 +2,112 @@ import GroupService from '../services/group.js';
|
||||
import UserService from '../services/user.js';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import config from '../../config.json' with {type: "json"};
|
||||
import statuses from '../utils/status.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";
|
||||
|
||||
class GroupController {
|
||||
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 status = await GroupService.create(groupName, user.login.id);
|
||||
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
||||
let group = await GroupService.create(groupName, user.login.id);
|
||||
|
||||
log.info(`New group with name ${groupName} was just created by user ${user.login.username}`);
|
||||
|
||||
UserService.joinGroup(user.login.id, status.id);
|
||||
return res.status(200).send("Successfull");
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/create: ${e}`)); }
|
||||
await UserService.joinGroup(user.login.id, group.id);
|
||||
return res.status(200).send(group.id.toString())
|
||||
}
|
||||
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 status = await UserService.joinGroup(user.login.id, groupId);
|
||||
let user = jwt.decode(req.headers.authorization.split(' ')[1], config.secret);
|
||||
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}`);
|
||||
return res.status(200).send("Successfull");
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/join: ${e}`)); }
|
||||
async leave(req, res) {
|
||||
let groupId = req.params.groupId;
|
||||
|
||||
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) {
|
||||
try {
|
||||
let { groupId } = req.params;
|
||||
let { password } = req.body;
|
||||
let { groupId } = req.params;
|
||||
let { password } = req.body;
|
||||
|
||||
await GroupService.updatePassword(groupId, password);
|
||||
log.info(`Password for group with ID ${groupId} was updated`);
|
||||
return res.status(200).send("Successfull");
|
||||
await GroupService.updatePassword(groupId, password);
|
||||
log.info(`Password for group with ID ${groupId} was updated`);
|
||||
|
||||
} 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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,58 @@
|
||||
import ProductService from '../services/product.js';
|
||||
import statuses from '../utils/status.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/product.js"
|
||||
|
||||
class AbstractProductController {
|
||||
async create(req, res) {
|
||||
try {
|
||||
const { groupId, localId, abstract_product_id, amount, date_of_production, expiry_date } = req.body;
|
||||
await ProductService.create(groupId, localId, abstract_product_id, amount, date_of_production, expiry_date);
|
||||
return res.status(200).send("Successfull");
|
||||
} catch (e) {
|
||||
switch (e.status) {
|
||||
case statuses.duplicate:
|
||||
return res.status(400).send(e.message);
|
||||
default:
|
||||
log.error(e.original)
|
||||
return res.status(500).send(e.message)
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
let data = await ProductService.getByLocalId(groupId, localId)
|
||||
|
||||
notify(req.headers.authorization.split(' ')[1], groupId, 'create', 'product', data);
|
||||
|
||||
return res.status(200).send(translate(req.headers["accept-language"], responseCodes.responses.general.ok));
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
} catch (e) {
|
||||
switch (e.status) {
|
||||
case statuses.invalid_syntax:
|
||||
log.error(e.original);
|
||||
return res.status(400).send(e.message);
|
||||
}
|
||||
}
|
||||
if (expiry_date) await ProductService.updateExpiryDate(groupId, localId, expiry_date);
|
||||
|
||||
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 log from '../utils/log.js';
|
||||
import bcrypt from 'bcrypt';
|
||||
import genToken from '../utils/jwt.js';
|
||||
import jwtutils from '../utils/jwt.js';
|
||||
import AbstractProductService from '../services/abstractproduct.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"
|
||||
|
||||
class UserController {
|
||||
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`);
|
||||
return res.status(200).send("Successfull register");
|
||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/register: ${e}`)); }
|
||||
log.info(`New user with name ${username} has just registered`);
|
||||
return res.status(200).send(userId.toString());
|
||||
}
|
||||
|
||||
async login(req, res) {
|
||||
try {
|
||||
const { username, password } = req.body;
|
||||
const { username, password } = req.body;
|
||||
|
||||
const user = await UserService.getByUsername(username);
|
||||
if (!bcrypt.compareSync(password, user.password)) return res.status(401).send("Wrong password");
|
||||
const user = await UserService.getByUsername(username);
|
||||
if (!bcrypt.compareSync(password, user.password)) throw new customError(`Wrong user password`, responseCodes.responses.passwords.invalid);
|
||||
|
||||
const token = genToken(user);
|
||||
return res.status(200).send(token);
|
||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/login: ${e}`)); }
|
||||
const token = jwtutils.genToken(user);
|
||||
return res.status(200).send({ id: user.id, token: token });
|
||||
}
|
||||
|
||||
async synchronize(req, res) {
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { groupId } = req.params;
|
||||
|
||||
let result = {};
|
||||
result.abstract_products = await AbstractProductService.getAll(groupId);
|
||||
result.products = await ProductService.getAll(groupId);
|
||||
result.categories = await CategoryService.getAll(groupId);
|
||||
let result = {};
|
||||
result.abstract_products = await AbstractProductService.getAll(groupId);
|
||||
result.abstract_products.forEach((abstractproduct) => {
|
||||
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);
|
||||
} catch (e) { res.status(500).send(log.unknownError(`${TAG}/synchronize: ${e}`)); }
|
||||
result.products = await ProductService.getAll(groupId);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
64
src/index.js
64
src/index.js
@@ -1,5 +1,6 @@
|
||||
import express from 'express';
|
||||
import UserRouter from './routers/user.js';
|
||||
import UserService from './services/user.js';
|
||||
import GroupRouter from './routers/group.js';
|
||||
import AbstractProductRouter from './routers/abstractproduct.js';
|
||||
import log from './utils/log.js';
|
||||
@@ -8,7 +9,13 @@ import config from '../config.json' with {type: "json"};
|
||||
import ProductRouter from './routers/product.js';
|
||||
import CategoryRouter from './routers/category.js';
|
||||
|
||||
import { WebSocketServer } from 'ws';
|
||||
import jwt from 'jsonwebtoken';
|
||||
|
||||
const app = express();
|
||||
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 }));
|
||||
@@ -19,6 +26,59 @@ app.use('/api/abstractproduct', AbstractProductRouter);
|
||||
app.use('/api/product', ProductRouter);
|
||||
app.use('/api/category', CategoryRouter);
|
||||
|
||||
app.listen(config.port, () => {
|
||||
log.info(`Application has started on port ${config.port}`)
|
||||
app.get('/status', (req, res) => {
|
||||
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 config from '../../config.json' with {type: "json"};
|
||||
import GroupService from '../services/group.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 requireUsername = async (req, res, next) => {
|
||||
if (req.method == "OPTIONS") next();
|
||||
|
||||
try {
|
||||
const { username } = req.body;
|
||||
if (!username) return res.status(400).send("Username is required");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requireUsername: ${e}`)); }
|
||||
const { username } = req.body;
|
||||
if (!username) throw new customError(`requireUsername username is required`, responseCodes.responses.usernames.required)
|
||||
next();
|
||||
};
|
||||
|
||||
const requirePassword = async (req, res, next) => {
|
||||
if (req.method == "OPTIONS") next();
|
||||
|
||||
try {
|
||||
const { password } = req.body;
|
||||
if (!password) return res.status(400).send("Password is required");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(unknownError(`${TAG}/requirePassword: ${e}`)); }
|
||||
const { password } = req.body;
|
||||
if (!password) throw new customError(`requirePassword password is required`, responseCodes.responses.passwords.required);
|
||||
next();
|
||||
};
|
||||
|
||||
const authenticate = async (req, res, next) => {
|
||||
if (req.method == "OPTIONS") next();
|
||||
if (!req.headers.authorization) throw new customError(`authenticate no authorization header`, responseCodes.responses.authentication.not_found);
|
||||
|
||||
try {
|
||||
if (!req.headers.authorization) return res.status(403).send("No authorization header supplied");
|
||||
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");
|
||||
const token = req.headers.authorization.split(' ')[1];
|
||||
if (!token) throw new customError(`authenticate no authorization token in header`, responseCodes.responses.authentication.not_found);
|
||||
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/authenticate: ${e}`)); }
|
||||
let user = jwt.decode(token, config.secret);
|
||||
|
||||
await UserService.getByUsername(user.login.username)
|
||||
|
||||
if (!jwt.verify(token, config.secret)) throw new customError(`authenticate token is invalid`, responseCodes.responses.authentication.invalid);
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
const authorizeGroupOwner = async (req, res, 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);
|
||||
if (user.login.id != adminId) return res.status(403).send("Not your group");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/authorizeGroupOwner: ${e}`)); }
|
||||
let adminId = await GroupService.getAdminId(groupId);
|
||||
if (user.login.id != adminId) throw new customError(`authorizeGroupOwner not an owner`, responseCodes.responses.groups.not_an_owner)
|
||||
next();
|
||||
};
|
||||
|
||||
const checkGroupPassword = async (req, res, next) => {
|
||||
if (req.method == "OPTIONS") next();
|
||||
|
||||
try {
|
||||
const { groupId } = req.params;
|
||||
const { password } = req.body;
|
||||
const { groupId } = req.params;
|
||||
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");
|
||||
next();
|
||||
if (groupPassword != password) throw new customError(`checkGroupPassword password is invalid`, responseCodes.responses.passwords.invalid);
|
||||
next();
|
||||
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/checkGroupPassword: ${e}`)); }
|
||||
};
|
||||
|
||||
const userIsInGroup = async (req, res, next) => {
|
||||
@@ -77,7 +73,7 @@ const userIsInGroup = async (req, res, next) => {
|
||||
|
||||
const token = req.headers.authorization.split(' ')[1];
|
||||
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();
|
||||
};
|
||||
|
||||
|
||||
@@ -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 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) => {
|
||||
try {
|
||||
let groupId = req.params.groupId || req.body.groupId;
|
||||
let groupId = Number(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");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupExists: ${e}`)) }
|
||||
let group = await GroupService.getById(groupId);
|
||||
|
||||
if (!group) throw new customError(`group does not exists`, responseCodes.responses.groups.id_not_found);
|
||||
next();
|
||||
};
|
||||
|
||||
const groupDoesntExist = async (req, res, next) => {
|
||||
try {
|
||||
let groupId = req.params.groupId || req.body.groupId;
|
||||
const groupNameExists = async (req, res, next) => {
|
||||
let groupName = req.params.groupName || req.body.groupName;
|
||||
|
||||
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");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupDoesntExist: ${e}`)) }
|
||||
if (!group) throw new customError(`group does not exists`, responseCodes.responses.groups.name_not_found);
|
||||
next();
|
||||
};
|
||||
|
||||
const groupNameDoesntExist = async (req, res, next) => {
|
||||
try {
|
||||
const { groupName } = req.params;
|
||||
const localIdExists = async (req, res, next) => {
|
||||
let localId = req.params.localId || req.body.localId;
|
||||
|
||||
let group = await GroupService.getByName(groupName);
|
||||
if (group) return res.status(400).send("Such group name already exists");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/groupNameDoesntExist: ${e}`)); }
|
||||
};
|
||||
if (!localId) throw new customError(`local id is not specified`, responseCodes.responses.general.invalid_syntax);
|
||||
next();
|
||||
}
|
||||
|
||||
const abstractProductExists = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId, localId } = req.body;
|
||||
|
||||
let result = await AbstractProductService.exists(groupId, localId);
|
||||
if (!result) return res.status(404).send("Abstract product not found");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/abstractProductExists: ${e}`)); }
|
||||
};
|
||||
|
||||
const productExists = async (req, res, next) => {
|
||||
try {
|
||||
const { groupId, localId } = req.body;
|
||||
|
||||
let result = await ProductService.exists(groupId, localId);
|
||||
if (!result) return res.status(404).send("Product not found");
|
||||
next();
|
||||
} catch (e) { return res.status(500).send(log.unknownError(`${TAG}/productExists: ${e}`)); }
|
||||
};
|
||||
|
||||
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
|
||||
};
|
||||
export default { groupExists, localIdExists, groupNameExists }
|
||||
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,9 +1,10 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../middlewares/auth.js';
|
||||
import AbstractProductController from '../controllers/abstractproduct.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
import multer from 'multer';
|
||||
import path from 'path';
|
||||
import tryHandler from '../response/errorHandler.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
|
||||
const upload = multer(({
|
||||
dest: path.join(path.resolve(path.dirname('')), "/temp")
|
||||
@@ -11,7 +12,10 @@ const upload = multer(({
|
||||
|
||||
const AbstractProductRouter = new Router();
|
||||
|
||||
AbstractProductRouter.post('/create', upload.single("file"), auth.authenticate, existance.groupExists, auth.userIsInGroup, AbstractProductController.create);
|
||||
AbstractProductRouter.post('/update', upload.single("file"), auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.abstractProductExists, AbstractProductController.update);
|
||||
AbstractProductRouter.post('/create', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(AbstractProductController.create));
|
||||
AbstractProductRouter.post('/update', upload.single("file"), tryHandler(auth.authenticate), tryHandler(auth.userIsInGroup), tryHandler(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;
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../middlewares/auth.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
import CategoryController from '../controllers/category.js';
|
||||
import tryHandler from '../response/errorHandler.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
|
||||
const CategoryRouter = new Router();
|
||||
|
||||
CategoryRouter.post('/create', auth.authenticate, existance.groupExists, existance.categoryNameDoesntExist, CategoryController.create);
|
||||
CategoryRouter.post('/update', auth.authenticate, existance.groupExists, existance.categoryExists, CategoryController.update);
|
||||
CategoryRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(CategoryController.create));
|
||||
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;
|
||||
@@ -1,12 +1,19 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../middlewares/auth.js';
|
||||
import GroupController from '../controllers/group.js';
|
||||
import tryHandler from '../response/errorHandler.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
|
||||
const GroupRouter = new Router();
|
||||
|
||||
GroupRouter.post('/create/:groupName', auth.authenticate, existance.groupNameDoesntExist, GroupController.create);
|
||||
GroupRouter.post('/join/:groupId', auth.authenticate, existance.groupExists, auth.requirePassword, auth.checkGroupPassword, GroupController.join);
|
||||
GroupRouter.post('/password/:groupId', auth.authenticate, existance.groupExists, auth.authorizeGroupOwner, auth.requirePassword, GroupController.updatePassword);
|
||||
|
||||
GroupRouter.post('/create/:groupName', tryHandler(auth.authenticate), tryHandler(GroupController.create));
|
||||
GroupRouter.post('/join/:groupId', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.requirePassword), tryHandler(auth.checkGroupPassword), tryHandler(GroupController.join));
|
||||
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;
|
||||
@@ -1,11 +1,14 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../middlewares/auth.js';
|
||||
import ProductController from '../controllers/product.js'
|
||||
import tryHandler from '../response/errorHandler.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
|
||||
const ProductRouter = new Router();
|
||||
|
||||
ProductRouter.post('/create', auth.authenticate, existance.groupExists, auth.userIsInGroup, ProductController.create);
|
||||
ProductRouter.post('/update', auth.authenticate, existance.groupExists, auth.userIsInGroup, existance.productExists, ProductController.update);
|
||||
ProductRouter.post('/create', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.create));
|
||||
ProductRouter.post('/update', tryHandler(auth.authenticate), tryHandler(existance.groupExists), tryHandler(existance.localIdExists), tryHandler(auth.userIsInGroup), tryHandler(ProductController.update));
|
||||
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;
|
||||
@@ -1,12 +1,17 @@
|
||||
import { Router } from 'express';
|
||||
import auth from '../middlewares/auth.js';
|
||||
import existance from '../middlewares/existance.js';
|
||||
import UserController from '../controllers/user.js';
|
||||
import tryHandler from '../response/errorHandler.js';
|
||||
|
||||
const UserRouter = new Router();
|
||||
|
||||
UserRouter.post('/register', auth.requireUsername, auth.requirePassword, existance.usernameDoesntExist, UserController.register);
|
||||
UserRouter.post('/login', auth.requireUsername, auth.requirePassword, existance.usernameExists, UserController.login);
|
||||
UserRouter.get('/synchronize/:groupId', auth.authenticate, existance.groupExists, auth.userIsInGroup, UserController.synchronize);
|
||||
UserRouter.post('/register', tryHandler(auth.requireUsername), tryHandler(auth.requirePassword), tryHandler(UserController.register));
|
||||
UserRouter.post('/login', tryHandler(auth.requireUsername), tryHandler(auth.requirePassword), tryHandler(UserController.login));
|
||||
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;
|
||||
@@ -1,12 +1,14 @@
|
||||
import ProductService from './product.js';
|
||||
import db from '../db.js';
|
||||
import statuses from '../utils/status.js';
|
||||
import errorHandler from '../utils/pgerrorhandler.js';
|
||||
import responses from '../response/responseCodes.js';
|
||||
import customError from '../response/customError.js';
|
||||
|
||||
class AbstractProductService {
|
||||
async create(groupid, localid, barcode, name, net_weight, image_filename, category, unit) {
|
||||
await db.query("INSERT INTO abstract_products (group_id, local_id, barcode, name, net_weight, image_filename, category, unit) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", [groupid, localid, barcode, name, net_weight, image_filename, category, unit])
|
||||
.catch((e) => {
|
||||
errorHandler(e, "abstract product")
|
||||
errorHandler(e, "abstractproduct")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,21 +22,21 @@ class AbstractProductService {
|
||||
async updateName(groupId, localId, name) {
|
||||
await db.query("UPDATE abstract_products SET name = $1 WHERE group_id = $2 AND local_id = $3", [name, groupId, localId])
|
||||
.catch((e) => {
|
||||
errorHandler(e, "name")
|
||||
errorHandler(e, "abstractproductname")
|
||||
});
|
||||
}
|
||||
|
||||
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) => {
|
||||
errorHandler(e, "net weight")
|
||||
}));
|
||||
errorHandler(e, "netweight")
|
||||
});
|
||||
}
|
||||
|
||||
async updateImageFilename(groupId, localId, image_filename) {
|
||||
await db.query("UPDATE abstract_products SET image_filename = $1 WHERE group_id = $2 AND local_id = $3", [image_filename, groupId, localId])
|
||||
.catch((e) => {
|
||||
errorHandler(e, "image filename")
|
||||
errorHandler(e, "imagehash")
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +56,7 @@ class AbstractProductService {
|
||||
|
||||
async getAll(groupId) {
|
||||
let result = (await db.query("SELECT local_id, barcode, name, net_weight, image_filename, category, unit FROM abstract_products WHERE group_id = $1", [groupId])).rows;
|
||||
if (!result) return statuses.not_found;
|
||||
// if (!result) throw new customError("getAll abstract products not found", responses.responses.abstractproducts.not_found)
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -63,6 +65,25 @@ class AbstractProductService {
|
||||
if (!result) return false;
|
||||
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();
|
||||
@@ -1,5 +1,9 @@
|
||||
import ProductService from "./product.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 {
|
||||
async create(groupId, categoryId, name) {
|
||||
@@ -11,16 +15,31 @@ class CategoryService {
|
||||
}
|
||||
|
||||
async getById(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;
|
||||
let result = (await db.query("SELECT * FROM categories WHERE group_id = $1 AND local_id = $2", [groupId, localId]));
|
||||
if (result.rowCount == 0) throw new customError(`getById categorirs not found`, responseCodes.responses.category.not_found)
|
||||
return result.rows[0];
|
||||
}
|
||||
|
||||
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]));
|
||||
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];
|
||||
}
|
||||
|
||||
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();
|
||||
@@ -1,17 +1,18 @@
|
||||
import db from '../db.js';
|
||||
import customError from '../response/customError.js';
|
||||
import responseCodes from '../response/responseCodes.js';
|
||||
import errorHandler from '../utils/pgerrorhandler.js';
|
||||
import status from '../utils/status.js';
|
||||
|
||||
class GroupService {
|
||||
async create(name, creatorId) {
|
||||
let res = await db.query("INSERT INTO groups (name, admin_id) VALUES ($1, $2) RETURNING ID", [name, creatorId]).catch(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];
|
||||
}
|
||||
|
||||
async getById(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];
|
||||
}
|
||||
|
||||
@@ -28,7 +29,21 @@ class GroupService {
|
||||
}
|
||||
|
||||
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 statuses from '../utils/status.js';
|
||||
import errorHandler from '../utils/pgerrorhandler.js';
|
||||
import customError from '../response/customError.js';
|
||||
import responseCodes from '../response/responseCodes.js';
|
||||
|
||||
class ProductService {
|
||||
async create(groupid, localid, abstract_product_id, amount, date_of_production, expiry_date) {
|
||||
await db.query("INSERT INTO products (group_id, local_id, abstract_product_id, amount, date_of_production, expiry_date) VALUES ($1, $2, $3, $4, $5, $6)", [groupid, localid, abstract_product_id, amount, date_of_production, expiry_date])
|
||||
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) => {
|
||||
errorHandler(e, "Abstract Product")
|
||||
errorHandler(e, "product")
|
||||
});
|
||||
}
|
||||
|
||||
async updateAbstractProductId(groupId, localId, abstract_product_id) {
|
||||
await db.query("UPDATE products SET abstract_product_id = $1 WHERE group_id = $2 AND local_id = $3", [abstract_product_id, groupId, localId])
|
||||
.catch((e) => {
|
||||
errorHandler(e, "abstract product id")
|
||||
errorHandler(e, "abstractproductid")
|
||||
});
|
||||
|
||||
}
|
||||
@@ -26,22 +27,22 @@ class ProductService {
|
||||
}
|
||||
|
||||
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) => {
|
||||
errorHandler(e, "date of production")
|
||||
errorHandler(e, "dateofproduction")
|
||||
});
|
||||
}
|
||||
|
||||
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) => {
|
||||
errorHandler(e, "expiry date")
|
||||
errorHandler(e, "expirydate")
|
||||
});
|
||||
}
|
||||
|
||||
async getAll(groupId) {
|
||||
let result = (await db.query("SELECT local_id, abstract_product_id, amount, date_of_production, expiry_date FROM products WHERE group_id = $1", [groupId])).rows;
|
||||
if (!result) return statuses.not_found;
|
||||
// if (!result) throw new customError(`getAll product not found`, responseCodes.responses.products.not_found);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -50,6 +51,21 @@ class ProductService {
|
||||
if (!result) return false;
|
||||
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();
|
||||
@@ -1,23 +1,28 @@
|
||||
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 errorHandler from '../utils/pgerrorhandler.js';
|
||||
|
||||
class UserService {
|
||||
async create(username, password) {
|
||||
await db.query("INSERT INTO users (username, password) VALUES ($1, $2)", [username, bcrypt.hashSync(password, 12)]).catch((e) => {
|
||||
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");
|
||||
})
|
||||
return statuses.ok;
|
||||
})).rows[0].id
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
async getByUsername(username) {
|
||||
let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username])).rows;
|
||||
if (user == undefined) return statuses.not_found;
|
||||
return (user[0]);
|
||||
let user = (await db.query("SELECT * FROM Users WHERE username = $1", [username]));
|
||||
if (user.rowCount == 0) throw new customError(`getByUsername user not found`, responseCodes.responses.usernames.not_found);
|
||||
return user.rows[0];
|
||||
}
|
||||
|
||||
async getAll() {
|
||||
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) {
|
||||
@@ -25,9 +30,30 @@ class UserService {
|
||||
}
|
||||
|
||||
async joinGroup(userId, groupId) {
|
||||
if (await (this.isInGroup(userId, groupId))) return statuses.duplicate;
|
||||
await db.query("UPDATE Users SET groups = array_append(groups, $1::integer) WHERE ID = $2", [groupId, userId]);
|
||||
return statuses.ok;
|
||||
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]);
|
||||
}
|
||||
|
||||
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" });
|
||||
};
|
||||
|
||||
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) => {
|
||||
if (config.debug) console.debug(`[D] [${Date()}]: ${text}`);
|
||||
}
|
||||
};
|
||||
|
||||
const info = (text) => {
|
||||
console.log(`[I] [${Date()}]: ${text}`);
|
||||
}
|
||||
};
|
||||
|
||||
const error = (text) => {
|
||||
console.error(`[E] [${Date()}]: ${text}`);
|
||||
}
|
||||
};
|
||||
|
||||
const warn = (text) => {
|
||||
console.warn(`[W] [${Date()}]: ${text}`);
|
||||
}
|
||||
};
|
||||
|
||||
const unknownError = (text) => {
|
||||
error(text);
|
||||
return "Unknown server error. Please, report to the developer";
|
||||
}
|
||||
};
|
||||
|
||||
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,28 +1,17 @@
|
||||
import statuses from "./status.js";
|
||||
import customError from "../response/customError.js";
|
||||
import responseCodes from "../response/responseCodes.js";
|
||||
|
||||
const errorHandler = (e, obj) => {
|
||||
switch (e.code) {
|
||||
case '23505':
|
||||
throw {
|
||||
status: statuses.duplicate,
|
||||
message: `Such ${obj} already exists`
|
||||
};
|
||||
throw new customError(`Duplicate ${obj}`, responseCodes.responses[obj].duplicate)
|
||||
case '22007':
|
||||
throw {
|
||||
status: statuses.invalid_syntax,
|
||||
message: `Invalid syntax in ${obj}`
|
||||
};
|
||||
throw new customError(`Invalid syntax ${obj}`, responseCodes.responses.general.invalid_syntax)
|
||||
case '22001':
|
||||
throw {
|
||||
status: statuses.invalid_syntax,
|
||||
message: `Value too long (${obj})`
|
||||
};
|
||||
throw new customError(`Value too long ${obj}`, responseCodes.responses[obj].too_long)
|
||||
default:
|
||||
throw {
|
||||
status: statuses.unknown,
|
||||
message: `Unknown error. Please, report to the developer`,
|
||||
original: e
|
||||
};
|
||||
console.log(e)
|
||||
throw new customError(`Unknown error ${obj}`, responseCodes.responses.general.unknown)
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -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