serverest 3.1.0 → 3.1.1

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.
@@ -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.1.0",
3
+ "version": "3.1.1",
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/customization')
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,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 carrinhoDoUsuario = await carrinhosService.getAll({ produtos: { $elemMatch: { idProduto: req.params.id } } })
38
- const usuarioTemCarrinho = carrinhoDoUsuario.length > 0
39
- if (usuarioTemCarrinho) {
40
- const idCarrinhos = carrinhoDoUsuario.map(carrinhos => carrinhos._id)
41
- return res.status(400).send({ message: constant.DELETE_PRODUCT_WITH_CART, idCarrinhos })
42
- }
43
- const quantidadeRegistrosExcluidos = await service.deleteById(req.params.id)
44
- const message = quantidadeRegistrosExcluidos === 0 ? constant.DELETE_NONE : constant.DELETE_SUCCESS
45
- res.status(200).send({ message })
46
- }
47
-
48
- exports.put = async (req, res) => {
49
- const { id } = req.params
50
- const { nome } = req.body
51
- const product = { nome: nome.trim(), $not: { _id: id } }
52
-
53
- const productExists = await service.existeProduto(product)
54
-
55
- if (productExists) {
56
- return res.status(400).json({ message: constant.NAME_ALREADY_USED })
57
- }
58
-
59
- const updatedProduct = await service.createOrUpdateById(id, req.body)
60
- if (updatedProduct._id !== id) {
61
- return res.status(201).json({ message: constant.POST_SUCCESS, _id: updatedProduct._id })
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 carrinhoDoUsuario = await carrinhosService.getAll({ produtos: { $elemMatch: { idProduto: req.params.id } } })
38
+ const usuarioTemCarrinho = carrinhoDoUsuario.length > 0
39
+ if (usuarioTemCarrinho) {
40
+ const idCarrinhos = carrinhoDoUsuario.map(carrinhos => carrinhos._id)
41
+ return res.status(400).send({ message: constant.DELETE_PRODUCT_WITH_CART, idCarrinhos })
42
+ }
43
+ const quantidadeRegistrosExcluidos = await service.deleteById(req.params.id)
44
+ const message = quantidadeRegistrosExcluidos === 0 ? constant.DELETE_NONE : constant.DELETE_SUCCESS
45
+ res.status(200).send({ message })
46
+ }
47
+
48
+ exports.put = async (req, res) => {
49
+ const { id } = req.params
50
+ const { nome } = req.body
51
+ const product = { nome: nome.trim(), $not: { _id: id } }
52
+
53
+ const productExists = await service.existeProduto(product)
54
+
55
+ if (productExists) {
56
+ return res.status(400).json({ message: constant.NAME_ALREADY_USED })
57
+ }
58
+
59
+ const updatedProduct = await service.createOrUpdateById(id, req.body)
60
+ if (updatedProduct._id !== id) {
61
+ return res.status(201).json({ message: constant.POST_SUCCESS, _id: updatedProduct._id })
62
+ }
63
+
64
+ return res.status(200).json({ message: constant.PUT_SUCCESS })
65
+ }
@@ -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
+ }
@@ -0,0 +1,55 @@
1
+ const flagUrls = {
2
+ 'pt-BR': '/flags/flag_brazil.svg',
3
+ en: '/flags/flag_uk.svg',
4
+ es: '/flags/flag_peru.svg'
5
+ }
6
+
7
+ const supportedLanguages = [
8
+ { code: 'pt-BR', label: 'Português do Brasil' },
9
+ { code: 'es', label: 'Español' },
10
+ { code: 'en', label: 'English' }
11
+ ]
12
+
13
+ function getPreferredLanguage () {
14
+ return window.localStorage.getItem('swaggerLanguage') || 'pt-BR'
15
+ }
16
+
17
+ function setPreferredLanguage (code) {
18
+ window.localStorage.setItem('swaggerLanguage', code)
19
+ }
20
+
21
+ function getLanguageFromQuery () {
22
+ const url = new URL(window.location.href)
23
+ return url.searchParams.get('lang') || ''
24
+ }
25
+
26
+ function getCurrentLanguageCode () {
27
+ const queryLang = getLanguageFromQuery()
28
+ const preferred = getPreferredLanguage()
29
+ return supportedLanguages.some(l => l.code === queryLang)
30
+ ? queryLang
31
+ : (supportedLanguages.some(l => l.code === preferred) ? preferred : 'pt-BR')
32
+ }
33
+
34
+ function preloadFlags () {
35
+ if (typeof document === 'undefined' || !document.head) return
36
+ Object.values(flagUrls).forEach(href => {
37
+ const link = document.createElement('link')
38
+ link.rel = 'preload'
39
+ link.as = 'image'
40
+ link.href = href
41
+ document.head.appendChild(link)
42
+ })
43
+ }
44
+
45
+ function preloadSwaggerJson () {
46
+ if (typeof document === 'undefined' || !document.head) return
47
+ const code = getCurrentLanguageCode()
48
+ const href = '/swagger.json?lang=' + encodeURIComponent(code)
49
+ const link = document.createElement('link')
50
+ link.rel = 'preload'
51
+ link.as = 'fetch'
52
+ link.href = href
53
+ link.crossOrigin = ''
54
+ document.head.appendChild(link)
55
+ }
@@ -0,0 +1,158 @@
1
+ const releaseToastTranslations = {
2
+ 'pt-BR': {
3
+ title: 'Nova release disponível',
4
+ link: 'Ver release',
5
+ closeLabel: 'Fechar aviso de release',
6
+ meta: (latest, current) => 'v' + latest + ' (v' + current + ' instalada)'
7
+ },
8
+ en: {
9
+ title: 'New release available',
10
+ link: 'View release',
11
+ closeLabel: 'Close release notice',
12
+ meta: (latest, current) => 'v' + latest + ' (v' + current + ' installed)'
13
+ },
14
+ es: {
15
+ title: 'Nueva versión disponible',
16
+ link: 'Ver versión',
17
+ closeLabel: 'Cerrar aviso de versión',
18
+ meta: (latest, current) => 'v' + latest + ' (v' + current + ' instalada)'
19
+ }
20
+ }
21
+
22
+ function getReleaseToastTranslation (language) {
23
+ return releaseToastTranslations[language] || releaseToastTranslations['pt-BR']
24
+ }
25
+
26
+ function renderReleaseToast (releaseDataOverride) {
27
+ const info = releaseDataOverride !== undefined ? releaseDataOverride : releaseInfo
28
+ const current = normalizeVersion(currentVersion)
29
+ let latestVersion = normalizeVersion(info && (info.version || info.tag))
30
+ if (forceBanner) {
31
+ latestVersion = normalizeVersion('999.999.999')
32
+ }
33
+ if (!latestVersion || (!current && !forceBanner)) return
34
+ if (latestVersion === current && !forceBanner) return
35
+ if (document.querySelector('.release-toast')) return
36
+ const root = document.querySelector('.swagger-ui') || document.body
37
+
38
+ const toast = document.createElement('div')
39
+ toast.className = 'release-toast'
40
+ toast.setAttribute('aria-live', 'polite')
41
+ toast.dataset.latestVersion = latestVersion
42
+ toast.dataset.currentVersion = current
43
+
44
+ const content = document.createElement('div')
45
+ content.className = 'release-toast__content'
46
+
47
+ const title = document.createElement('div')
48
+ title.className = 'release-toast__title'
49
+ const titleIcon = document.createElement('span')
50
+ titleIcon.className = 'release-toast__title-icon'
51
+ titleIcon.textContent = '⚡'
52
+ const translation = getReleaseToastTranslation(getPreferredLanguage())
53
+ const titleText = document.createElement('span')
54
+ titleText.textContent = translation.title
55
+ title.appendChild(titleIcon)
56
+ title.appendChild(titleText)
57
+
58
+ const meta = document.createElement('div')
59
+ meta.className = 'release-toast__meta'
60
+ meta.textContent = translation.meta(latestVersion, current)
61
+
62
+ const link = document.createElement('a')
63
+ link.className = 'release-toast__link'
64
+ link.href = (info && info.url) ? info.url : 'https://github.com/ServeRest/ServeRest/releases'
65
+ link.textContent = translation.link
66
+ link.target = '_blank'
67
+ link.rel = 'noopener noreferrer'
68
+
69
+ const close = document.createElement('button')
70
+ close.type = 'button'
71
+ close.className = 'release-toast__close'
72
+ close.setAttribute('aria-label', translation.closeLabel)
73
+ close.textContent = '×'
74
+ close.addEventListener('click', function () {
75
+ toast.remove()
76
+ })
77
+
78
+ content.appendChild(title)
79
+ content.appendChild(meta)
80
+ content.appendChild(link)
81
+ toast.appendChild(content)
82
+ toast.appendChild(close)
83
+ root.appendChild(toast)
84
+ }
85
+
86
+ function updateReleaseToastLanguage (language) {
87
+ const toast = document.querySelector('.release-toast')
88
+ if (!toast) return
89
+ const translation = getReleaseToastTranslation(language)
90
+ const latestVersion = toast.dataset.latestVersion || ''
91
+ const current = toast.dataset.currentVersion || ''
92
+ const title = toast.querySelector('.release-toast__title span:last-child')
93
+ const meta = toast.querySelector('.release-toast__meta')
94
+ const link = toast.querySelector('.release-toast__link')
95
+ const close = toast.querySelector('.release-toast__close')
96
+ if (title) title.textContent = translation.title
97
+ if (meta) meta.textContent = translation.meta(latestVersion, current)
98
+ if (link) link.textContent = translation.link
99
+ if (close) close.setAttribute('aria-label', translation.closeLabel)
100
+ }
101
+
102
+ const RELEASE_CACHE_KEY = 'serverest_github_release'
103
+ const RELEASE_CACHE_TTL_MS = 10 * 60 * 1000
104
+
105
+ function getCachedRelease (requireFresh) {
106
+ try {
107
+ const raw = window.localStorage.getItem(RELEASE_CACHE_KEY)
108
+ if (!raw) return null
109
+ const parsed = JSON.parse(raw)
110
+ if (!parsed || !parsed.data) return null
111
+ if (requireFresh && (!parsed.cachedAt || (Date.now() - parsed.cachedAt) > RELEASE_CACHE_TTL_MS)) return null
112
+ return parsed.data
113
+ } catch (_) {
114
+ return null
115
+ }
116
+ }
117
+
118
+ function runReleaseCheck () {
119
+ renderReleaseToast()
120
+ const cached = getCachedRelease(true)
121
+ if (cached && cached.tag_name && cached.html_url) {
122
+ renderReleaseToast({
123
+ tag: cached.tag_name,
124
+ version: normalizeVersion(cached.tag_name),
125
+ url: cached.html_url
126
+ })
127
+ return
128
+ }
129
+ fetch('https://api.github.com/repos/ServeRest/ServeRest/releases/latest', {
130
+ headers: { Accept: 'application/json' }
131
+ })
132
+ .then(r => r.json())
133
+ .then(data => {
134
+ if (data.tag_name && data.html_url) {
135
+ try {
136
+ window.localStorage.setItem(RELEASE_CACHE_KEY, JSON.stringify({
137
+ data: { tag_name: data.tag_name, html_url: data.html_url },
138
+ cachedAt: Date.now()
139
+ }))
140
+ } catch (_) {}
141
+ renderReleaseToast({
142
+ tag: data.tag_name,
143
+ version: normalizeVersion(data.tag_name),
144
+ url: data.html_url
145
+ })
146
+ }
147
+ })
148
+ .catch(() => {
149
+ const stale = getCachedRelease(false)
150
+ if (stale && stale.tag_name && stale.html_url) {
151
+ renderReleaseToast({
152
+ tag: stale.tag_name,
153
+ version: normalizeVersion(stale.tag_name),
154
+ url: stale.html_url
155
+ })
156
+ }
157
+ })
158
+ }
@@ -0,0 +1,92 @@
1
+ /* uiLabelTranslations injetado no build a partir de src/swagger/translations/*.js */
2
+ const uiLabelTranslations = [
3
+ __UI_LABEL_TRANSLATIONS__
4
+ ]
5
+
6
+ const buildExactMap = (targetLang) => {
7
+ const map = new Map()
8
+ uiLabelTranslations.forEach(labels => {
9
+ const target = labels[targetLang]
10
+ if (!target) return
11
+ Object.keys(labels).forEach(sourceLang => {
12
+ if (sourceLang === targetLang) return
13
+ const source = labels[sourceLang]
14
+ if (!source || source === target) return
15
+ map.set(source, target)
16
+ })
17
+ })
18
+ return map
19
+ }
20
+
21
+ const uiTranslations = {
22
+ 'pt-BR': {
23
+ exact: buildExactMap('pt-BR'),
24
+ replace: [
25
+ { regex: /\binteger\b/g, value: 'inteiro' }
26
+ ]
27
+ },
28
+ en: {
29
+ exact: buildExactMap('en'),
30
+ replace: []
31
+ },
32
+ es: {
33
+ exact: buildExactMap('es'),
34
+ replace: [
35
+ { regex: /\binteger\b/g, value: 'entero' }
36
+ ]
37
+ }
38
+ }
39
+
40
+ let originalTextNodes = new WeakMap()
41
+
42
+ function resetTranslationCache () {
43
+ originalTextNodes = new WeakMap()
44
+ }
45
+
46
+ function shouldTranslateNode (node) {
47
+ if (!node || !node.nodeValue || !node.nodeValue.trim()) return false
48
+ const parent = node.parentElement
49
+ if (!parent) return false
50
+ if (parent.closest('.lang-switcher')) return false
51
+ return !parent.closest('code, pre, textarea, input')
52
+ }
53
+
54
+ function translateNodeText (node, language) {
55
+ const config = uiTranslations[language] || uiTranslations['pt-BR']
56
+ if (!originalTextNodes.has(node)) {
57
+ originalTextNodes.set(node, node.nodeValue)
58
+ }
59
+ const original = originalTextNodes.get(node)
60
+ const trimmed = original.trim()
61
+ if (config.exact.has(trimmed)) {
62
+ const updated = original.replace(trimmed, config.exact.get(trimmed))
63
+ if (updated !== node.nodeValue) {
64
+ node.nodeValue = updated
65
+ }
66
+ return
67
+ }
68
+ let updated = original
69
+ config.replace.forEach(({ regex, value }) => {
70
+ updated = updated.replace(regex, value)
71
+ })
72
+ if (updated !== original) {
73
+ node.nodeValue = updated
74
+ return
75
+ }
76
+ if (node.nodeValue !== original) {
77
+ node.nodeValue = original
78
+ }
79
+ }
80
+
81
+ function applyTranslations (language) {
82
+ const root = document.querySelector('.swagger-ui')
83
+ if (!root || !window.NodeFilter || !document.createTreeWalker) return
84
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
85
+ acceptNode: node => (shouldTranslateNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT)
86
+ })
87
+ const nodes = []
88
+ while (walker.nextNode()) {
89
+ nodes.push(walker.currentNode)
90
+ }
91
+ nodes.forEach(node => translateNodeText(node, language))
92
+ }