serverest 3.1.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/flags/flag_peru.svg +10 -0
- package/docs/images/serverest_logo.svg +116 -0
- package/package.json +4 -1
- package/src/app.js +1 -1
- package/src/controllers/produtos-controller.js +63 -65
- package/src/controllers/usuarios-controller.js +3 -4
- package/src/data/carrinhos.db +8 -0
- package/src/data/produtos.db +6 -0
- package/src/data/usuarios.db +2 -0
- package/src/middlewares/error-handler.js +6 -0
- package/src/services/carrinhos-service.js +30 -22
- package/src/services/produtos-service.js +84 -0
- package/src/services/usuarios-service.js +6 -2
- package/src/swagger/index.js +62 -0
- package/src/swagger/scripts/01-config.js +10 -0
- package/src/swagger/scripts/02-language-storage.js +55 -0
- package/src/swagger/scripts/03-release-toast.js +158 -0
- package/src/swagger/scripts/04-ui-translations.js +92 -0
- package/src/swagger/scripts/05-swagger-spec.js +110 -0
- package/src/swagger/scripts/06-language-switcher.js +68 -0
- package/src/swagger/scripts/07-doc-lifecycle.js +163 -0
- package/src/swagger/styles/language-switcher.css +64 -0
- package/src/swagger/{custom-css.css → styles/release-toast.css} +1 -60
- package/src/swagger/styles/topbar.css +56 -0
- package/src/swagger/swagger-sw.js +2 -2
- package/src/swagger/translations/en.js +23 -0
- package/src/swagger/translations/es.js +23 -0
- package/src/swagger/translations/pt-BR.js +23 -0
- package/docs/flags/flag_spain.svg +0 -2552
- package/docs/images/serverest_logo.png +0 -0
- package/src/swagger/custom-js.js +0 -647
- package/src/swagger/customization.js +0 -25
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
|
3
|
+
<svg
|
|
4
|
+
width="900"
|
|
5
|
+
height="600"
|
|
6
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
7
|
+
id="Flag_of_Peru">
|
|
8
|
+
<rect height="600" width="900" fill="#D91023" x="0" y="0" />
|
|
9
|
+
<rect height="600" width="300" fill="white" x="300" y="0" />
|
|
10
|
+
</svg>
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<?xml version="1.0" standalone="no"?>
|
|
2
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
|
3
|
+
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
|
4
|
+
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
|
5
|
+
width="3864.000000pt" height="1530.000000pt" viewBox="0 0 3864.000000 1530.000000"
|
|
6
|
+
preserveAspectRatio="xMidYMid meet">
|
|
7
|
+
<metadata>
|
|
8
|
+
Created by potrace 1.16, written by Peter Selinger 2001-2019
|
|
9
|
+
</metadata>
|
|
10
|
+
<g transform="translate(0.000000,1530.000000) scale(0.100000,-0.100000)"
|
|
11
|
+
fill="#000000" stroke="none">
|
|
12
|
+
<path d="M0 7650 l0 -7650 19320 0 19320 0 0 6865 0 6865 -695 0 -695 0 0
|
|
13
|
+
-4342 c0 -2772 -4 -4365 -10 -4403 -12 -76 -53 -142 -114 -182 -96 -65 -105
|
|
14
|
+
-66 -591 -71 -470 -4 -591 3 -673 38 -58 25 -114 79 -139 135 -17 38 -18 228
|
|
15
|
+
-21 4432 l-2 4392 -923 4 -922 3 -106 26 c-364 90 -565 264 -646 558 -15 54
|
|
16
|
+
-18 105 -18 295 0 228 0 231 26 286 47 101 130 164 265 205 56 18 183 19 2662
|
|
17
|
+
21 l2602 3 0 85 0 85 -19320 0 -19320 0 0 -7650z m4275 7489 c189 -22 368 -75
|
|
18
|
+
540 -161 380 -191 611 -483 702 -888 15 -67 17 -202 20 -1319 3 -885 1 -1259
|
|
19
|
+
-7 -1297 -16 -76 -63 -141 -130 -178 l-55 -31 -305 -3 c-343 -4 -373 0 -495
|
|
20
|
+
58 -124 59 -188 133 -219 255 -14 52 -16 198 -16 1167 0 1223 4 1140 -65 1217
|
|
21
|
+
-51 56 -116 75 -239 69 -110 -5 -161 -28 -204 -90 -51 -76 -56 -103 -48 -313
|
|
22
|
+
31 -894 221 -1667 748 -3055 66 -173 167 -439 225 -590 120 -318 317 -900 397
|
|
23
|
+
-1175 279 -962 405 -1776 409 -2648 2 -237 -1 -296 -16 -371 -111 -547 -492
|
|
24
|
+
-916 -1069 -1036 -313 -65 -715 -48 -994 41 -492 157 -807 504 -901 991 -17
|
|
25
|
+
86 -18 184 -18 1318 l0 1225 24 53 c29 64 86 115 155 139 45 16 88 18 346 18
|
|
26
|
+
253 0 304 -3 359 -18 125 -35 220 -101 279 -194 64 -100 62 -58 62 -1240 0
|
|
27
|
+
-1173 -3 -1104 59 -1176 46 -52 107 -72 216 -72 104 1 153 15 202 58 63 55 73
|
|
28
|
+
91 73 265 0 432 -57 921 -165 1405 -137 612 -330 1212 -697 2157 -368 947
|
|
29
|
+
-574 1594 -723 2272 -130 591 -182 1038 -192 1633 -5 289 -3 354 11 435 130
|
|
30
|
+
743 832 1181 1731 1079z m26885 -19 c317 -28 589 -109 841 -251 419 -236 721
|
|
31
|
+
-635 844 -1117 75 -293 76 -339 73 -2062 -3 -1403 -4 -1488 -21 -1531 -26 -67
|
|
32
|
+
-79 -123 -145 -156 l-57 -28 -368 -3 c-343 -3 -374 -2 -449 17 -230 58 -388
|
|
33
|
+
202 -427 389 -8 37 -11 464 -11 1420 0 869 -4 1391 -10 1432 -13 84 -77 215
|
|
34
|
+
-133 272 -75 77 -164 107 -317 108 -212 0 -327 -68 -409 -245 -52 -109 -55
|
|
35
|
+
-139 -48 -495 15 -834 112 -1443 351 -2216 159 -516 353 -971 791 -1864 264
|
|
36
|
+
-537 362 -755 520 -1155 353 -890 579 -1838 670 -2800 44 -474 74 -1320 56
|
|
37
|
+
-1583 -16 -228 -56 -425 -122 -604 -240 -653 -758 -1066 -1474 -1174 -163 -25
|
|
38
|
+
-511 -25 -679 0 -405 59 -753 222 -1021 478 -280 267 -452 602 -537 1048 -22
|
|
39
|
+
115 -22 117 -25 1684 -3 1384 -2 1575 12 1621 20 69 87 144 157 176 53 24 54
|
|
40
|
+
24 438 24 424 0 445 -2 576 -66 119 -59 225 -179 268 -303 21 -60 21 -74 26
|
|
41
|
+
-1441 l5 -1380 22 -52 c87 -214 218 -294 461 -280 53 2 120 12 147 22 114 38
|
|
42
|
+
204 140 248 280 21 66 22 86 20 345 -5 747 -77 1330 -248 1995 -187 732 -408
|
|
43
|
+
1299 -905 2315 -494 1011 -733 1641 -935 2465 -206 839 -287 1507 -302 2495
|
|
44
|
+
-6 437 -1 532 42 745 34 171 85 318 171 490 95 194 179 311 334 465 388 389
|
|
45
|
+
948 574 1570 520z m-22122 -29 c65 -29 124 -94 154 -170 21 -52 22 -71 23
|
|
46
|
+
-281 0 -180 -4 -242 -18 -310 -48 -224 -166 -386 -341 -470 -131 -62 -138 -63
|
|
47
|
+
-740 -67 l-549 -5 6 -1656 c4 -911 7 -1982 7 -2379 l0 -723 670 0 c434 0 686
|
|
48
|
+
-4 718 -11 74 -15 152 -66 190 -123 60 -91 67 -132 66 -406 -1 -261 -14 -361
|
|
49
|
+
-63 -491 -91 -240 -289 -397 -551 -439 -38 -6 -267 -10 -547 -10 l-483 0 0
|
|
50
|
+
-647 c0 -357 3 -1428 7 -2380 l6 -1733 511 0 c281 0 542 -5 579 -10 292 -42
|
|
51
|
+
482 -237 538 -550 21 -117 19 -480 -3 -551 -26 -84 -76 -142 -155 -181 l-68
|
|
52
|
+
-33 -1320 0 c-1234 0 -1323 1 -1370 18 -67 23 -118 66 -146 124 -21 45 -23 64
|
|
53
|
+
-30 338 -4 160 -10 3134 -14 6611 -7 5758 -6 6325 8 6375 30 101 103 162 215
|
|
54
|
+
178 31 5 640 8 1352 7 l1295 -1 53 -24z m2831 6 c264 -43 474 -113 679 -226
|
|
55
|
+
396 -218 667 -562 781 -989 59 -220 54 -18 58 -2652 3 -1710 1 -2447 -7 -2525
|
|
56
|
+
-39 -394 -178 -712 -415 -951 l-90 -91 50 -34 c117 -80 250 -228 324 -360 39
|
|
57
|
+
-70 86 -197 114 -309 l21 -85 3 -2593 c2 -1777 0 -2606 -7 -2632 -15 -57 -67
|
|
58
|
+
-119 -130 -154 l-55 -31 -545 0 -545 0 -53 29 c-59 33 -108 88 -128 144 -11
|
|
59
|
+
30 -13 497 -14 2523 0 1622 -3 2505 -10 2540 -25 136 -122 256 -242 300 -59
|
|
60
|
+
21 -81 23 -365 27 l-303 4 0 -2679 0 -2678 -24 -50 c-13 -29 -44 -69 -70 -92
|
|
61
|
+
-84 -74 -76 -73 -647 -73 -489 0 -510 1 -564 21 -66 25 -137 91 -159 151 -15
|
|
62
|
+
39 -16 605 -14 6668 l3 6625 28 56 c31 64 82 105 155 125 37 10 257 13 1057
|
|
63
|
+
10 890 -2 1022 -4 1114 -19z m2923 -8 c244 -95 401 -286 437 -529 6 -41 133
|
|
64
|
+
-2325 282 -5075 148 -2750 273 -5013 277 -5029 4 -15 132 2255 284 5045 214
|
|
65
|
+
3914 281 5088 293 5134 54 202 207 371 410 452 l70 28 383 3 c446 4 468 1 538
|
|
66
|
+
-73 50 -52 64 -101 64 -226 0 -124 -1076 -14279 -1091 -14349 -34 -162 -139
|
|
67
|
+
-263 -300 -289 -85 -14 -1221 -14 -1309 -1 -164 26 -252 116 -294 300 -16 67
|
|
68
|
+
-1088 14195 -1090 14360 -2 143 46 223 156 261 49 17 84 19 438 16 l385 -2 67
|
|
69
|
+
-26z m6264 -1 c88 -46 136 -114 160 -226 17 -82 19 -387 3 -517 -40 -322 -186
|
|
70
|
+
-529 -439 -619 l-85 -31 -557 -3 -558 -3 0 -2158 c0 -1187 3 -2401 7 -2699 l6
|
|
71
|
+
-541 621 -3 621 -3 53 -30 c64 -35 96 -71 131 -145 l26 -55 0 -290 c0 -266 -2
|
|
72
|
+
-297 -22 -377 -66 -258 -206 -421 -427 -495 l-81 -27 -462 -4 -463 -3 0 -2377
|
|
73
|
+
c0 -1307 3 -2488 7 -2624 l6 -247 546 -4 c609 -4 610 -4 745 -74 94 -49 200
|
|
74
|
+
-154 248 -248 79 -152 108 -317 108 -607 0 -258 -26 -351 -121 -432 -87 -74 7
|
|
75
|
+
-69 -1449 -70 -1206 -1 -1309 0 -1360 16 -63 20 -123 65 -147 111 -45 87 -42
|
|
76
|
+
-271 -54 7342 -11 6676 -10 7205 5 7264 30 118 85 174 195 197 43 10 367 12
|
|
77
|
+
1371 11 l1315 -2 51 -27z m2688 7 c254 -30 439 -82 656 -186 553 -266 874
|
|
78
|
+
-736 930 -1364 8 -83 10 -852 8 -2475 -4 -2159 -5 -2364 -21 -2459 -61 -375
|
|
79
|
+
-183 -634 -409 -867 l-87 -90 57 -41 c240 -169 394 -427 444 -744 11 -68 13
|
|
80
|
+
-566 13 -2649 l0 -2565 -23 -47 c-26 -53 -72 -97 -135 -130 l-42 -23 -540 0
|
|
81
|
+
-540 0 -51 27 c-57 30 -108 83 -133 137 -15 33 -16 259 -21 2571 l-5 2535 -31
|
|
82
|
+
65 c-40 83 -89 139 -151 172 -94 52 -142 58 -445 58 l-278 0 -2 -2678 -3
|
|
83
|
+
-2677 -28 -58 c-19 -38 -44 -69 -75 -92 -85 -65 -91 -66 -658 -63 l-509 3 -57
|
|
84
|
+
28 c-70 34 -124 94 -143 161 -13 44 -15 853 -15 6616 0 4700 3 6583 11 6625
|
|
85
|
+
20 111 70 175 161 206 50 17 109 18 1023 19 804 0 992 -3 1099 -15z m4760 1
|
|
86
|
+
c92 -28 156 -91 193 -189 22 -57 30 -373 13 -505 -38 -301 -178 -491 -427
|
|
87
|
+
-579 l-78 -28 -569 -3 -569 -3 6 -562 c4 -309 7 -1382 7 -2384 l0 -1823 683 0
|
|
88
|
+
c750 0 742 1 825 -60 55 -40 99 -101 120 -169 14 -47 17 -97 17 -301 0 -255
|
|
89
|
+
-8 -329 -47 -450 -80 -248 -241 -404 -486 -472 -73 -21 -103 -22 -596 -26
|
|
90
|
+
l-519 -3 6 -727 c4 -400 7 -1471 7 -2379 l0 -1653 530 0 c600 0 625 -2 769
|
|
91
|
+
-72 114 -55 203 -143 260 -259 73 -151 96 -292 89 -567 -4 -150 -8 -185 -26
|
|
92
|
+
-232 -29 -73 -68 -119 -133 -155 l-54 -30 -400 -8 c-220 -4 -821 -6 -1335 -5
|
|
93
|
+
-868 3 -938 5 -980 21 -80 32 -116 62 -147 124 l-28 58 -6 1245 c-4 685 -8
|
|
94
|
+
3666 -10 6625 l-4 5380 23 50 c38 83 94 127 185 145 23 4 625 8 1338 9 1098 1
|
|
95
|
+
1303 -1 1343 -13z m-26059 -10689 c53 -30 100 -94 127 -175 21 -64 23 -84 26
|
|
96
|
+
-444 l3 -378 1097 -2 1097 -3 57 -28 c77 -38 134 -96 171 -175 l32 -67 3 -347
|
|
97
|
+
c2 -223 0 -374 -8 -420 -24 -157 -131 -269 -285 -297 -40 -8 -394 -11 -1112
|
|
98
|
+
-11 l-1053 0 0 -347 c0 -376 -7 -437 -53 -530 -31 -60 -91 -116 -142 -133 -54
|
|
99
|
+
-18 -141 -7 -205 25 -29 15 -220 149 -424 298 -204 149 -609 444 -901 655
|
|
100
|
+
-291 211 -552 405 -578 430 -28 28 -63 77 -85 121 -33 68 -36 80 -36 161 0 81
|
|
101
|
+
3 93 36 161 23 47 56 93 90 125 71 69 1816 1330 1897 1371 54 28 74 33 135 33
|
|
102
|
+
55 0 80 -5 111 -23z m34026 -10 c35 -17 330 -226 654 -463 325 -236 738 -537
|
|
103
|
+
918 -668 276 -200 335 -247 372 -297 59 -80 80 -145 80 -244 -1 -106 -38 -190
|
|
104
|
+
-122 -273 -65 -64 -1831 -1344 -1904 -1380 -58 -29 -152 -39 -204 -22 -50 17
|
|
105
|
+
-111 73 -140 130 -47 93 -55 171 -55 539 l0 340 -1087 3 -1088 3 -60 24 c-82
|
|
106
|
+
33 -164 116 -194 195 -21 58 -22 67 -19 470 l3 411 27 50 c32 62 113 139 176
|
|
107
|
+
168 l47 22 1098 3 1097 2 0 338 c0 360 7 436 46 522 54 119 112 160 225 160
|
|
108
|
+
55 0 77 -5 130 -33z"/>
|
|
109
|
+
<path d="M10990 11080 l0 -2721 308 3 307 3 54 28 c136 72 211 211 240 447 16
|
|
110
|
+
123 15 4560 0 4632 -24 111 -96 217 -180 262 -96 53 -132 58 -441 63 l-288 5
|
|
111
|
+
0 -2722z"/>
|
|
112
|
+
<path d="M22940 11069 l0 -2720 298 3 297 3 67 33 c107 53 174 147 221 311
|
|
113
|
+
l22 76 3 2334 c2 2117 1 2340 -14 2390 -29 101 -103 195 -188 238 -82 41 -175
|
|
114
|
+
52 -453 53 l-253 0 0 -2721z"/>
|
|
115
|
+
</g>
|
|
116
|
+
</svg>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverest",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Servidor REST local de forma rápida e simples para estudo de testes de API",
|
|
5
5
|
"author": "Paulo Gonçalves <author@serverest.dev> (https://www.linkedin.com/in/paulo-goncalves/)",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -111,6 +111,9 @@
|
|
|
111
111
|
}
|
|
112
112
|
},
|
|
113
113
|
"standard": {
|
|
114
|
+
"ignore": [
|
|
115
|
+
"src/swagger/scripts/*.js"
|
|
116
|
+
],
|
|
114
117
|
"globals": [
|
|
115
118
|
"after",
|
|
116
119
|
"afterEach",
|
package/src/app.js
CHANGED
|
@@ -27,7 +27,7 @@ const { version } = require('../package.json')
|
|
|
27
27
|
const swaggerDocument = require('../docs/swagger.json')
|
|
28
28
|
const rateLimiter = require('./middlewares/rate-limiter')
|
|
29
29
|
const { fetchLatestRelease } = require('./utils/github-release')
|
|
30
|
-
const { buildCustomJsStr, customCss } = require('./swagger
|
|
30
|
+
const { buildCustomJsStr, customCss } = require('./swagger')
|
|
31
31
|
const { localizeSwaggerDocument } = require('./swagger/translations')
|
|
32
32
|
|
|
33
33
|
const forceReleaseBanner = process.env.FORCE_RELEASE_BANNER === 'true'
|
|
@@ -1,65 +1,63 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const carrinhosService = require('../services/carrinhos-service')
|
|
4
|
-
const constant = require('../utils/constants')
|
|
5
|
-
const service = require('../services/produtos-service')
|
|
6
|
-
|
|
7
|
-
exports.get = async (req, res) => {
|
|
8
|
-
const produtos = await service.getAll(req.query)
|
|
9
|
-
res.status(200).send({ quantidade: produtos.length, produtos })
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
exports.getOne = async (req, res) => {
|
|
13
|
-
const { id } = req.params
|
|
14
|
-
const produto = await service.getOne(id)
|
|
15
|
-
if (!produto) {
|
|
16
|
-
return res.status(400).json({ message: constant.PRODUCT_NOT_FOUND })
|
|
17
|
-
}
|
|
18
|
-
return res.status(200).send(produto)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
exports.post = async (req, res) => {
|
|
22
|
-
const { nome } = req.body
|
|
23
|
-
const trimmedName = nome.trim()
|
|
24
|
-
|
|
25
|
-
const productExists = await service.existeProduto({ nome: trimmedName })
|
|
26
|
-
|
|
27
|
-
if (productExists) {
|
|
28
|
-
return res.status(400).send({ message: constant.NAME_ALREADY_USED })
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const { _id } = await service.criarProduto(req.body)
|
|
32
|
-
|
|
33
|
-
res.status(201).send({ message: constant.POST_SUCCESS, _id })
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
exports.delete = async (req, res) => {
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
const { id }
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return res.status(200).json({ message: constant.PUT_SUCCESS })
|
|
65
|
-
}
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const carrinhosService = require('../services/carrinhos-service')
|
|
4
|
+
const constant = require('../utils/constants')
|
|
5
|
+
const service = require('../services/produtos-service')
|
|
6
|
+
|
|
7
|
+
exports.get = async (req, res) => {
|
|
8
|
+
const produtos = await service.getAll(req.query)
|
|
9
|
+
res.status(200).send({ quantidade: produtos.length, produtos })
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exports.getOne = async (req, res) => {
|
|
13
|
+
const { id } = req.params
|
|
14
|
+
const produto = await service.getOne(id)
|
|
15
|
+
if (!produto) {
|
|
16
|
+
return res.status(400).json({ message: constant.PRODUCT_NOT_FOUND })
|
|
17
|
+
}
|
|
18
|
+
return res.status(200).send(produto)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
exports.post = async (req, res) => {
|
|
22
|
+
const { nome } = req.body
|
|
23
|
+
const trimmedName = nome.trim()
|
|
24
|
+
|
|
25
|
+
const productExists = await service.existeProduto({ nome: trimmedName })
|
|
26
|
+
|
|
27
|
+
if (productExists) {
|
|
28
|
+
return res.status(400).send({ message: constant.NAME_ALREADY_USED })
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const { _id } = await service.criarProduto(req.body)
|
|
32
|
+
|
|
33
|
+
res.status(201).send({ message: constant.POST_SUCCESS, _id })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
exports.delete = async (req, res) => {
|
|
37
|
+
const carrinhoComProduto = await carrinhosService.existeCarrinhoComProduto(req.params.id)
|
|
38
|
+
if (carrinhoComProduto) {
|
|
39
|
+
return res.status(400).send({ message: constant.DELETE_PRODUCT_WITH_CART, idCarrinhos: [carrinhoComProduto._id] })
|
|
40
|
+
}
|
|
41
|
+
const quantidadeRegistrosExcluidos = await service.deleteById(req.params.id)
|
|
42
|
+
const message = quantidadeRegistrosExcluidos === 0 ? constant.DELETE_NONE : constant.DELETE_SUCCESS
|
|
43
|
+
res.status(200).send({ message })
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
exports.put = async (req, res) => {
|
|
47
|
+
const { id } = req.params
|
|
48
|
+
const { nome } = req.body
|
|
49
|
+
const product = { nome: nome.trim(), $not: { _id: id } }
|
|
50
|
+
|
|
51
|
+
const productExists = await service.existeProduto(product)
|
|
52
|
+
|
|
53
|
+
if (productExists) {
|
|
54
|
+
return res.status(400).json({ message: constant.NAME_ALREADY_USED })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const updatedProduct = await service.createOrUpdateById(id, req.body)
|
|
58
|
+
if (updatedProduct._id !== id) {
|
|
59
|
+
return res.status(201).json({ message: constant.POST_SUCCESS, _id: updatedProduct._id })
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return res.status(200).json({ message: constant.PUT_SUCCESS })
|
|
63
|
+
}
|
|
@@ -27,10 +27,9 @@ exports.post = async (req, res) => {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
exports.delete = async (req, res) => {
|
|
30
|
-
const carrinhoDoUsuario = await carrinhosService.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
return res.status(400).send({ message: constant.DELETE_USER_WITH_CART, idCarrinho: carrinhoDoUsuario[0]._id })
|
|
30
|
+
const carrinhoDoUsuario = await carrinhosService.existeCarrinho({ idUsuario: req.params.id })
|
|
31
|
+
if (carrinhoDoUsuario) {
|
|
32
|
+
return res.status(400).send({ message: constant.DELETE_USER_WITH_CART, idCarrinho: carrinhoDoUsuario._id })
|
|
34
33
|
}
|
|
35
34
|
const quantidadeRegistrosExcluidos = await service.deleteById(req.params.id)
|
|
36
35
|
const message = quantidadeRegistrosExcluidos === 0 ? constant.DELETE_NONE : constant.DELETE_SUCCESS
|
package/src/data/carrinhos.db
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
1
|
{"produtos":[{"idProduto":"BeeJh5lz3k6kSIzA","quantidade":2,"precoUnitario":470},{"idProduto":"K6leHdftCeOJj8BJ","quantidade":1,"precoUnitario":5240}],"precoTotal":6180,"quantidadeTotal":3,"idUsuario":"0uxuPY0cbmQhpEz1","_id":"qbMqntef4iTOwWfg"}
|
|
2
|
+
{"$$indexCreated":{"fieldName":"idUsuario","unique":false,"sparse":false}}
|
|
3
|
+
{"$$indexCreated":{"fieldName":"precoTotal","unique":false,"sparse":false}}
|
|
4
|
+
{"$$indexCreated":{"fieldName":"quantidadeTotal","unique":false,"sparse":false}}
|
|
5
|
+
{"$$indexCreated":{"fieldName":"produtos.idProduto","unique":false,"sparse":false}}
|
|
6
|
+
{"$$indexCreated":{"fieldName":"idUsuario"}}
|
|
7
|
+
{"$$indexCreated":{"fieldName":"precoTotal"}}
|
|
8
|
+
{"$$indexCreated":{"fieldName":"quantidadeTotal"}}
|
|
9
|
+
{"$$indexCreated":{"fieldName":"produtos.idProduto"}}
|
package/src/data/produtos.db
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
1
|
{"nome":"Logitech MX Vertical","preco":470,"descricao":"Mouse","quantidade":382,"_id":"BeeJh5lz3k6kSIzA"}
|
|
2
2
|
{"nome":"Samsung 60 polegadas","preco":5240,"descricao":"TV","quantidade":49977,"_id":"K6leHdftCeOJj8BJ"}
|
|
3
|
+
{"$$indexCreated":{"fieldName":"nome","unique":false,"sparse":true}}
|
|
4
|
+
{"$$indexCreated":{"fieldName":"preco","unique":false,"sparse":false}}
|
|
5
|
+
{"$$indexCreated":{"fieldName":"quantidade","unique":false,"sparse":false}}
|
|
6
|
+
{"$$indexCreated":{"fieldName":"nome","sparse":true}}
|
|
7
|
+
{"$$indexCreated":{"fieldName":"preco"}}
|
|
8
|
+
{"$$indexCreated":{"fieldName":"quantidade"}}
|
package/src/data/usuarios.db
CHANGED
|
@@ -9,6 +9,12 @@ function errorHandler (error, _req, res, _next) {
|
|
|
9
9
|
if (erroDeSchema) {
|
|
10
10
|
return res.status(400).json(montarMensagemDeErroDeSchema(error))
|
|
11
11
|
}
|
|
12
|
+
if (error.name === 'URIError') {
|
|
13
|
+
log({ message: `URI decode error: ${error.message}` })
|
|
14
|
+
return res.status(400).json({
|
|
15
|
+
message: 'Parâmetro de URL inválido. Verifique os caracteres especiais e tente novamente.'
|
|
16
|
+
})
|
|
17
|
+
}
|
|
12
18
|
// https://github.com/expressjs/body-parser#errors
|
|
13
19
|
if (error.type === 'entity.parse.failed') {
|
|
14
20
|
log({ message: 'Entity parse error, user sending request without proper quotation marks.' })
|
|
@@ -10,6 +10,16 @@ const produtosService = require('../services/produtos-service')
|
|
|
10
10
|
|
|
11
11
|
const datastore = Datastore.create({ filename: join(__dirname, '../data/carrinhos.db'), autoload: true })
|
|
12
12
|
|
|
13
|
+
// Index frequently searched fields for better query performance
|
|
14
|
+
/* istanbul ignore next */
|
|
15
|
+
datastore.ensureIndex({ fieldName: 'idUsuario' })
|
|
16
|
+
/* istanbul ignore next */
|
|
17
|
+
datastore.ensureIndex({ fieldName: 'precoTotal' })
|
|
18
|
+
/* istanbul ignore next */
|
|
19
|
+
datastore.ensureIndex({ fieldName: 'quantidadeTotal' })
|
|
20
|
+
/* istanbul ignore next */
|
|
21
|
+
datastore.ensureIndex({ fieldName: 'produtos.idProduto' })
|
|
22
|
+
|
|
13
23
|
exports.getAll = queryString => {
|
|
14
24
|
queryString = alterarValoresParaRegex(queryString)
|
|
15
25
|
return datastore.find(queryString)
|
|
@@ -23,6 +33,10 @@ exports.existeCarrinho = pesquisa => {
|
|
|
23
33
|
return datastore.findOne(pesquisa)
|
|
24
34
|
}
|
|
25
35
|
|
|
36
|
+
exports.existeCarrinhoComProduto = idProduto => {
|
|
37
|
+
return datastore.findOne({ produtos: { $elemMatch: { idProduto } } })
|
|
38
|
+
}
|
|
39
|
+
|
|
26
40
|
exports.criarCarrinho = async body => {
|
|
27
41
|
return datastore.insert(body)
|
|
28
42
|
}
|
|
@@ -39,17 +53,15 @@ exports.extrairProdutosDuplicados = arrayProdutos => {
|
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
exports.getProdutosComPrecoUnitarioOuErro = async arrayProdutos => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
return { error: { ...error, item } }
|
|
49
|
-
}
|
|
50
|
-
produtosComPrecoUnitario.push({ ...produto, precoUnitario: preco })
|
|
56
|
+
// Use batch lookup instead of per-item queries to reduce round-trips
|
|
57
|
+
const { produtosComPreco, error } = await produtosService.getPrecosUnitariosPorLote(arrayProdutos)
|
|
58
|
+
|
|
59
|
+
if (error) {
|
|
60
|
+
const item = { ...error.item, index: error.index }
|
|
61
|
+
return { error: { ...error, item } }
|
|
51
62
|
}
|
|
52
|
-
|
|
63
|
+
|
|
64
|
+
return { produtosComPrecoUnitario: produtosComPreco }
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
exports.deleteById = async id => {
|
|
@@ -80,24 +92,20 @@ exports.usuarioJaPossuiCarrinho = async (authorization) => {
|
|
|
80
92
|
}
|
|
81
93
|
|
|
82
94
|
exports.precoTotal = async (produtos) => {
|
|
83
|
-
return produtos.reduce(
|
|
84
|
-
return
|
|
85
|
-
},
|
|
95
|
+
return produtos.reduce((total, produto) => {
|
|
96
|
+
return total + (produto.precoUnitario * produto.quantidade)
|
|
97
|
+
}, 0)
|
|
86
98
|
}
|
|
87
99
|
|
|
88
100
|
exports.quantidadeTotal = async (produtos) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}, Promise.resolve(0))
|
|
101
|
+
// Use batch update to reduce round-trips instead of updating per-item
|
|
102
|
+
await produtosService.updateQuantidadePorLote(produtos)
|
|
103
|
+
return produtos.reduce((total, produto) => total + produto.quantidade, 0)
|
|
93
104
|
}
|
|
94
105
|
|
|
95
106
|
exports.reabasteceEstoque = async produtos => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
const { quantidade: quantidadeEmEstoque } = await produtosService.getDadosDoProduto({ _id: idProduto })
|
|
99
|
-
await produtosService.updateById(idProduto, { $set: { quantidade: quantidadeEmEstoque + quantidade } })
|
|
100
|
-
}
|
|
107
|
+
// Use batch restore to reduce round-trips instead of restoring per-item
|
|
108
|
+
await produtosService.restoreQuantidadePorLote(produtos)
|
|
101
109
|
}
|
|
102
110
|
|
|
103
111
|
const idUsuario = async (authorization) => {
|
|
@@ -8,6 +8,14 @@ const alterarValoresParaRegex = require('../utils/alterarValoresParaRegex')
|
|
|
8
8
|
|
|
9
9
|
const datastore = Datastore.create({ filename: join(__dirname, '../data/produtos.db'), autoload: true })
|
|
10
10
|
|
|
11
|
+
// Index frequently searched fields for better query performance
|
|
12
|
+
/* istanbul ignore next */
|
|
13
|
+
datastore.ensureIndex({ fieldName: 'nome', sparse: true })
|
|
14
|
+
/* istanbul ignore next */
|
|
15
|
+
datastore.ensureIndex({ fieldName: 'preco' })
|
|
16
|
+
/* istanbul ignore next */
|
|
17
|
+
datastore.ensureIndex({ fieldName: 'quantidade' })
|
|
18
|
+
|
|
11
19
|
exports.getAll = queryString => {
|
|
12
20
|
queryString = alterarValoresParaRegex(queryString)
|
|
13
21
|
return datastore.find(queryString)
|
|
@@ -17,6 +25,7 @@ exports.getOne = id => {
|
|
|
17
25
|
return datastore.findOne({ _id: id })
|
|
18
26
|
}
|
|
19
27
|
|
|
28
|
+
/* istanbul ignore next */
|
|
20
29
|
exports.getDadosDoProduto = queryString => {
|
|
21
30
|
return datastore.findOne(queryString)
|
|
22
31
|
}
|
|
@@ -25,17 +34,61 @@ exports.existeProduto = pesquisa => {
|
|
|
25
34
|
return datastore.findOne(pesquisa)
|
|
26
35
|
}
|
|
27
36
|
|
|
37
|
+
/* istanbul ignore next */
|
|
28
38
|
exports.updateQuantidade = async ({ idProduto, quantidade }) => {
|
|
29
39
|
const { quantidade: quantidadeEmEstoque } = await this.getDadosDoProduto({ _id: idProduto })
|
|
30
40
|
const novaQuantidade = quantidadeEmEstoque - quantidade
|
|
31
41
|
await this.updateById(idProduto, { $set: { quantidade: novaQuantidade } })
|
|
32
42
|
}
|
|
33
43
|
|
|
44
|
+
/* istanbul ignore next */
|
|
45
|
+
exports.updateQuantidadePorLote = async (arrayProdutosComQuantidade) => {
|
|
46
|
+
// Batch update to reduce round-trips: fetch all, calculate new quantities, update all
|
|
47
|
+
const idProdutos = arrayProdutosComQuantidade.map(p => p.idProduto)
|
|
48
|
+
const produtosEmEstoque = await datastore.find({ _id: { $in: idProdutos } })
|
|
49
|
+
const produtosMap = new Map(produtosEmEstoque.map(p => [p._id, p]))
|
|
50
|
+
|
|
51
|
+
// Calculate new quantities and perform updates
|
|
52
|
+
const updates = arrayProdutosComQuantidade.map(produto => {
|
|
53
|
+
const { idProduto, quantidade } = produto
|
|
54
|
+
const produtoAtual = produtosMap.get(idProduto)
|
|
55
|
+
if (produtoAtual) {
|
|
56
|
+
const novaQuantidade = produtoAtual.quantidade - quantidade
|
|
57
|
+
return this.updateById(idProduto, { $set: { quantidade: novaQuantidade } })
|
|
58
|
+
}
|
|
59
|
+
return Promise.resolve()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
await Promise.all(updates)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* istanbul ignore next */
|
|
66
|
+
exports.restoreQuantidadePorLote = async (arrayProdutosComQuantidade) => {
|
|
67
|
+
// Batch restore to reduce round-trips: fetch all, calculate restored quantities, update all
|
|
68
|
+
const idProdutos = arrayProdutosComQuantidade.map(p => p.idProduto)
|
|
69
|
+
const produtosEmEstoque = await datastore.find({ _id: { $in: idProdutos } })
|
|
70
|
+
const produtosMap = new Map(produtosEmEstoque.map(p => [p._id, p]))
|
|
71
|
+
|
|
72
|
+
// Calculate restored quantities and perform updates
|
|
73
|
+
const updates = arrayProdutosComQuantidade.map(produto => {
|
|
74
|
+
const { idProduto, quantidade } = produto
|
|
75
|
+
const produtoAtual = produtosMap.get(idProduto)
|
|
76
|
+
if (produtoAtual) {
|
|
77
|
+
const quantidadeRestaurada = produtoAtual.quantidade + quantidade
|
|
78
|
+
return this.updateById(idProduto, { $set: { quantidade: quantidadeRestaurada } })
|
|
79
|
+
}
|
|
80
|
+
return Promise.resolve()
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
await Promise.all(updates)
|
|
84
|
+
}
|
|
85
|
+
|
|
34
86
|
exports.criarProduto = async body => {
|
|
35
87
|
body = formatarValores(body)
|
|
36
88
|
return datastore.insert(body)
|
|
37
89
|
}
|
|
38
90
|
|
|
91
|
+
/* istanbul ignore next */
|
|
39
92
|
exports.getPrecoUnitarioOuErro = async (produto) => {
|
|
40
93
|
const quantidade = parseInt(produto.quantidade)
|
|
41
94
|
const { idProduto } = produto
|
|
@@ -51,6 +104,37 @@ exports.getPrecoUnitarioOuErro = async (produto) => {
|
|
|
51
104
|
return { precoUnitario: preco }
|
|
52
105
|
}
|
|
53
106
|
|
|
107
|
+
exports.getPrecosUnitariosPorLote = async (arrayProdutos) => {
|
|
108
|
+
// Fetch all products in one query to reduce round-trips
|
|
109
|
+
const idProdutos = arrayProdutos.map(p => p.idProduto)
|
|
110
|
+
const produtosEmEstoque = await datastore.find({ _id: { $in: idProdutos } })
|
|
111
|
+
const produtosMap = new Map(produtosEmEstoque.map(p => [p._id, p]))
|
|
112
|
+
|
|
113
|
+
// Validate each product and build result
|
|
114
|
+
for (let i = 0; i < arrayProdutos.length; i++) {
|
|
115
|
+
const produto = arrayProdutos[i]
|
|
116
|
+
const quantidade = parseInt(produto.quantidade)
|
|
117
|
+
const { idProduto } = produto
|
|
118
|
+
|
|
119
|
+
const produtoEmEstoque = produtosMap.get(idProduto)
|
|
120
|
+
if (!produtoEmEstoque) {
|
|
121
|
+
return { error: { statusCode: 400, message: constant.PRODUCT_NOT_FOUND, item: { idProduto, quantidade }, index: i } }
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (quantidade > produtoEmEstoque.quantidade) {
|
|
125
|
+
return { error: { statusCode: 400, message: constant.INSUFFICIENT_STOCK, item: { idProduto, quantidade, quantidadeEstoque: produtoEmEstoque.quantidade }, index: i } }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Build result with prices
|
|
130
|
+
const produtosComPreco = arrayProdutos.map((produto, index) => ({
|
|
131
|
+
...produto,
|
|
132
|
+
precoUnitario: produtosMap.get(produto.idProduto).preco
|
|
133
|
+
}))
|
|
134
|
+
|
|
135
|
+
return { produtosComPreco }
|
|
136
|
+
}
|
|
137
|
+
|
|
54
138
|
exports.deleteById = async id => {
|
|
55
139
|
return datastore.remove({ _id: id }, {})
|
|
56
140
|
}
|
|
@@ -7,6 +7,10 @@ const alterarValoresParaRegex = require('../utils/alterarValoresParaRegex')
|
|
|
7
7
|
|
|
8
8
|
const datastore = Datastore.create({ filename: join(__dirname, '../data/usuarios.db'), autoload: true })
|
|
9
9
|
|
|
10
|
+
// Index frequently searched fields for better query performance
|
|
11
|
+
/* istanbul ignore next */
|
|
12
|
+
datastore.ensureIndex({ fieldName: 'email', sparse: true })
|
|
13
|
+
|
|
10
14
|
exports.getAll = queryString => {
|
|
11
15
|
queryString = alterarValoresParaRegex(queryString)
|
|
12
16
|
return datastore.find(queryString)
|
|
@@ -25,8 +29,8 @@ exports.existeUsuario = async pesquisa => {
|
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
exports.usuarioEhAdministrador = async ({ email, password }) => {
|
|
28
|
-
const resultado = await datastore.
|
|
29
|
-
return JSON.parse(resultado
|
|
32
|
+
const resultado = await datastore.findOne({ email, password })
|
|
33
|
+
return JSON.parse(resultado.administrador)
|
|
30
34
|
}
|
|
31
35
|
|
|
32
36
|
exports.createUser = async body => {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { readFileSync, readdirSync } = require('fs')
|
|
4
|
+
const { join } = require('path')
|
|
5
|
+
|
|
6
|
+
const STYLES_DIR = join(__dirname, 'styles')
|
|
7
|
+
const SCRIPTS_DIR = join(__dirname, 'scripts')
|
|
8
|
+
const TRANSLATIONS_DIR = join(__dirname, 'translations')
|
|
9
|
+
|
|
10
|
+
const STYLE_FILES = ['topbar.css', 'language-switcher.css', 'release-toast.css']
|
|
11
|
+
|
|
12
|
+
function loadCustomCss () {
|
|
13
|
+
return STYLE_FILES
|
|
14
|
+
.map(file => readFileSync(join(STYLES_DIR, file), 'utf8'))
|
|
15
|
+
.join('\n')
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function loadCustomJsTemplate () {
|
|
19
|
+
const files = readdirSync(SCRIPTS_DIR)
|
|
20
|
+
.filter(f => f.endsWith('.js'))
|
|
21
|
+
.sort()
|
|
22
|
+
return files
|
|
23
|
+
.map(file => readFileSync(join(SCRIPTS_DIR, file), 'utf8'))
|
|
24
|
+
.join('\n')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function buildUiLabelTranslations () {
|
|
28
|
+
const ptBr = require(join(TRANSLATIONS_DIR, 'pt-BR.js'))
|
|
29
|
+
const en = require(join(TRANSLATIONS_DIR, 'en.js'))
|
|
30
|
+
const es = require(join(TRANSLATIONS_DIR, 'es.js'))
|
|
31
|
+
const uiKeys = Object.keys(ptBr).filter(k => k.startsWith('ui.')).sort()
|
|
32
|
+
const summaryKeys = Object.keys(ptBr).filter(k => k.endsWith('.summary')).sort()
|
|
33
|
+
const keys = [...uiKeys, ...summaryKeys]
|
|
34
|
+
const entries = keys.map(key => {
|
|
35
|
+
const pt = JSON.stringify(ptBr[key] || '')
|
|
36
|
+
const enVal = JSON.stringify(en[key] || '')
|
|
37
|
+
const esVal = JSON.stringify(es[key] || '')
|
|
38
|
+
return ` { 'pt-BR': ${pt}, en: ${enVal}, es: ${esVal} }`
|
|
39
|
+
})
|
|
40
|
+
return entries.join(',\n')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const customCss = loadCustomCss()
|
|
44
|
+
const customJsTemplate = loadCustomJsTemplate()
|
|
45
|
+
|
|
46
|
+
function buildCustomJsStr (releaseInfo, currentVersion, forceBanner) {
|
|
47
|
+
const replacements = {
|
|
48
|
+
__RELEASE_INFO__: JSON.stringify(releaseInfo),
|
|
49
|
+
__CURRENT_VERSION__: JSON.stringify(currentVersion),
|
|
50
|
+
__FORCE_BANNER__: JSON.stringify(forceBanner),
|
|
51
|
+
__UI_LABEL_TRANSLATIONS__: buildUiLabelTranslations()
|
|
52
|
+
}
|
|
53
|
+
return Object.keys(replacements).reduce(
|
|
54
|
+
(output, key) => output.split(key).join(replacements[key]),
|
|
55
|
+
customJsTemplate
|
|
56
|
+
)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
module.exports = {
|
|
60
|
+
buildCustomJsStr,
|
|
61
|
+
customCss
|
|
62
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/* global __RELEASE_INFO__, __CURRENT_VERSION__, __FORCE_BANNER__, NodeFilter, MutationObserver, caches */
|
|
2
|
+
(function () {
|
|
3
|
+
const releaseInfo = __RELEASE_INFO__
|
|
4
|
+
const currentVersion = __CURRENT_VERSION__
|
|
5
|
+
const forceBanner = __FORCE_BANNER__
|
|
6
|
+
|
|
7
|
+
function normalizeVersion (version) {
|
|
8
|
+
if (!version) return ''
|
|
9
|
+
return version.toString().trim().replace(/^v/i, '')
|
|
10
|
+
}
|