serverest 3.2.2 → 3.3.0-beta.2

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/README.md CHANGED
@@ -124,6 +124,50 @@ Em ambos os comandos de subida de ambiente local será utilizado a última vers
124
124
 
125
125
  Você pode encontrar as versões disponíveis na [lista de tags no Docker Hub](https://hub.docker.com/r/paulogoncalvesbh/serverest/tags) e na [lista de versões do NPM](https://www.npmjs.com/package/serverest).
126
126
 
127
+ ## Etapas da requisição
128
+
129
+ Toda resposta das rotas `/login`, `/usuarios`, `/produtos` e `/carrinhos` informa por quais etapas do servidor a requisição passou, quanto tempo cada uma levou e qual delas interrompeu a requisição. Assim é possível entender, sem ler o código, por que a resposta foi 401 e não 400 quando o token e o body estão errados ao mesmo tempo.
130
+
131
+ | Cabeçalho | Conteúdo |
132
+ |---|---|
133
+ | `X-ServeRest-Stages` | Todas as etapas da rota, em ordem de execução, com status, duração e detalhes |
134
+
135
+ A página de documentação desenha essas etapas logo abaixo da resposta de cada "Try it out": mostra onde a requisição parou, por que parou e como seguir, sem precisar ler o cabeçalho.
136
+
137
+ O cabeçalho é listado em `Access-Control-Expose-Headers`. Requisições `OPTIONS` (preflight de CORS) e as demais rotas (`/`, `/version`, `/swagger.json`) não possuem esse cabeçalho.
138
+
139
+ Exemplo de 401 sem token em `POST /carrinhos`:
140
+
141
+ ```
142
+ X-ServeRest-Stages: body;st=ok;dur=0.21, rate_limit;st=skipped, auth;st=failed;dur=0.05;reason=token_missing, schema;st=not_run, rules;st=not_run, write;st=not_run
143
+ ```
144
+
145
+ Cada entrada é um nome seguido de parâmetros `;chave=valor`, separadas por vírgula. Nome e valores usam apenas letras, números, `_` e `.`.
146
+
147
+ | Parâmetro | Valores | Presente quando |
148
+ |---|---|---|
149
+ | `st` | `ok`, `failed`, `skipped`, `not_run` | Sempre |
150
+ | `dur` | Milissegundos com duas casas decimais | A etapa executou (`ok` ou `failed`) |
151
+ | `reason` | `token_missing`, `bad_format`, `token_expired`, `token_invalid`, `user_not_found`, `not_admin` | A etapa `auth` falhou |
152
+ | `item` | Índice, a partir de zero, do item em `produtos` | Uma regra falhou em um item específico |
153
+
154
+ | Status | Significado |
155
+ |---|---|
156
+ | `ok` | Executou e deixou a requisição continuar |
157
+ | `failed` | Executou e interrompeu a requisição. No máximo uma etapa e uma regra por resposta |
158
+ | `skipped` | Não se aplica neste ambiente (rate limit ao executar localmente) |
159
+ | `not_run` | Não executou porque uma etapa anterior interrompeu a requisição |
160
+
161
+ As etapas são `body`, `rate_limit`, `auth`, `schema`, `read` (rotas GET), `rules` (regras de negócio e, enquanto a rota não for detalhada, também a gravação) e `write` (gravação, hoje apenas em `POST /carrinhos`). Entradas no formato `<etapa>.<regra>`, como `rules.enough_stock`, são regras da etapa citada antes do ponto: vêm logo depois dela e nunca possuem `dur`.
162
+
163
+ Casos que merecem atenção:
164
+
165
+ - Status 400 ou maior sem nenhuma etapa `failed` significa que a resposta veio de fora das etapas: rota inexistente (405) ou URL malformada (400).
166
+ - Em um timeout (408) a etapa que estava executando quando o tempo acabou é a marcada como `failed`.
167
+ - Em `POST /carrinhos` as regras `rules.product_exists` e `rules.enough_stock` são avaliadas item a item, na ordem do array, e param no primeiro item que falha. Portanto `rules.product_exists;st=ok` ao lado de `rules.enough_stock;st=failed;item=1` significa "nenhum produto inexistente até o item que falhou", e não "todos os produtos existem".
168
+
169
+ Evolução: versões futuras apenas adicionam entradas, como uma etapa `write` depois de `rules` ou entradas `rules.<regra>` em mais rotas. A gramática, os nomes dos parâmetros, os valores de status e os nomes das etapas acima são estáveis; renomear ou remover algum deles é uma quebra de compatibilidade.
170
+
127
171
  ## Teste de carga
128
172
 
129
173
  ### IMPORTANTE
package/docs/swagger.json CHANGED
@@ -52,6 +52,11 @@
52
52
  "responses": {
53
53
  "200": {
54
54
  "description": "paths.login.post.responses.200.description",
55
+ "headers": {
56
+ "X-ServeRest-Stages": {
57
+ "$ref": "#/components/headers/XServeRestStages"
58
+ }
59
+ },
55
60
  "content": {
56
61
  "application/json": {
57
62
  "schema": {
@@ -62,6 +67,11 @@
62
67
  },
63
68
  "401": {
64
69
  "description": "paths.login.post.responses.401.description",
70
+ "headers": {
71
+ "X-ServeRest-Stages": {
72
+ "$ref": "#/components/headers/XServeRestStages"
73
+ }
74
+ },
65
75
  "content": {
66
76
  "application/json": {
67
77
  "schema": {
@@ -99,6 +109,11 @@
99
109
  "responses": {
100
110
  "200": {
101
111
  "description": "paths.usuarios.get.responses.200.description",
112
+ "headers": {
113
+ "X-ServeRest-Stages": {
114
+ "$ref": "#/components/headers/XServeRestStages"
115
+ }
116
+ },
102
117
  "content": {
103
118
  "application/json": {
104
119
  "schema": {
@@ -121,6 +136,11 @@
121
136
  "responses": {
122
137
  "201": {
123
138
  "description": "paths.usuarios.post.responses.201.description",
139
+ "headers": {
140
+ "X-ServeRest-Stages": {
141
+ "$ref": "#/components/headers/XServeRestStages"
142
+ }
143
+ },
124
144
  "content": {
125
145
  "application/json": {
126
146
  "schema": {
@@ -156,6 +176,11 @@
156
176
  "responses": {
157
177
  "200": {
158
178
  "description": "paths.usuariosById.get.responses.200.description",
179
+ "headers": {
180
+ "X-ServeRest-Stages": {
181
+ "$ref": "#/components/headers/XServeRestStages"
182
+ }
183
+ },
159
184
  "content": {
160
185
  "application/json": {
161
186
  "schema": {
@@ -166,6 +191,11 @@
166
191
  },
167
192
  "400": {
168
193
  "description": "paths.usuariosById.get.responses.400.description",
194
+ "headers": {
195
+ "X-ServeRest-Stages": {
196
+ "$ref": "#/components/headers/XServeRestStages"
197
+ }
198
+ },
169
199
  "content": {
170
200
  "application/json": {
171
201
  "schema": {
@@ -203,6 +233,11 @@
203
233
  "responses": {
204
234
  "200": {
205
235
  "description": "paths.usuariosById.delete.responses.200.description",
236
+ "headers": {
237
+ "X-ServeRest-Stages": {
238
+ "$ref": "#/components/headers/XServeRestStages"
239
+ }
240
+ },
206
241
  "content": {
207
242
  "application/json": {
208
243
  "schema": {
@@ -219,6 +254,11 @@
219
254
  },
220
255
  "400": {
221
256
  "description": "paths.usuariosById.delete.responses.400.description",
257
+ "headers": {
258
+ "X-ServeRest-Stages": {
259
+ "$ref": "#/components/headers/XServeRestStages"
260
+ }
261
+ },
222
262
  "content": {
223
263
  "application/json": {
224
264
  "schema": {
@@ -253,6 +293,11 @@
253
293
  "responses": {
254
294
  "200": {
255
295
  "description": "paths.usuariosById.put.responses.200.description",
296
+ "headers": {
297
+ "X-ServeRest-Stages": {
298
+ "$ref": "#/components/headers/XServeRestStages"
299
+ }
300
+ },
256
301
  "content": {
257
302
  "application/json": {
258
303
  "schema": {
@@ -263,6 +308,11 @@
263
308
  },
264
309
  "201": {
265
310
  "description": "paths.usuariosById.put.responses.201.description",
311
+ "headers": {
312
+ "X-ServeRest-Stages": {
313
+ "$ref": "#/components/headers/XServeRestStages"
314
+ }
315
+ },
266
316
  "content": {
267
317
  "application/json": {
268
318
  "schema": {
@@ -303,6 +353,11 @@
303
353
  "responses": {
304
354
  "200": {
305
355
  "description": "paths.produtos.get.responses.200.description",
356
+ "headers": {
357
+ "X-ServeRest-Stages": {
358
+ "$ref": "#/components/headers/XServeRestStages"
359
+ }
360
+ },
306
361
  "content": {
307
362
  "application/json": {
308
363
  "schema": {
@@ -330,6 +385,11 @@
330
385
  "responses": {
331
386
  "201": {
332
387
  "description": "paths.produtos.post.responses.201.description",
388
+ "headers": {
389
+ "X-ServeRest-Stages": {
390
+ "$ref": "#/components/headers/XServeRestStages"
391
+ }
392
+ },
333
393
  "content": {
334
394
  "application/json": {
335
395
  "schema": {
@@ -371,6 +431,11 @@
371
431
  "responses": {
372
432
  "200": {
373
433
  "description": "paths.produtosById.get.responses.200.description",
434
+ "headers": {
435
+ "X-ServeRest-Stages": {
436
+ "$ref": "#/components/headers/XServeRestStages"
437
+ }
438
+ },
374
439
  "content": {
375
440
  "application/json": {
376
441
  "schema": {
@@ -381,6 +446,11 @@
381
446
  },
382
447
  "400": {
383
448
  "description": "paths.produtosById.get.responses.400.description",
449
+ "headers": {
450
+ "X-ServeRest-Stages": {
451
+ "$ref": "#/components/headers/XServeRestStages"
452
+ }
453
+ },
384
454
  "content": {
385
455
  "application/json": {
386
456
  "schema": {
@@ -423,6 +493,11 @@
423
493
  "responses": {
424
494
  "200": {
425
495
  "description": "paths.produtosById.delete.responses.200.description",
496
+ "headers": {
497
+ "X-ServeRest-Stages": {
498
+ "$ref": "#/components/headers/XServeRestStages"
499
+ }
500
+ },
426
501
  "content": {
427
502
  "application/json": {
428
503
  "schema": {
@@ -439,6 +514,11 @@
439
514
  },
440
515
  "400": {
441
516
  "description": "paths.produtosById.delete.responses.400.description",
517
+ "headers": {
518
+ "X-ServeRest-Stages": {
519
+ "$ref": "#/components/headers/XServeRestStages"
520
+ }
521
+ },
442
522
  "content": {
443
523
  "application/json": {
444
524
  "schema": {
@@ -484,6 +564,11 @@
484
564
  "responses": {
485
565
  "200": {
486
566
  "description": "paths.produtosById.put.responses.200.description",
567
+ "headers": {
568
+ "X-ServeRest-Stages": {
569
+ "$ref": "#/components/headers/XServeRestStages"
570
+ }
571
+ },
487
572
  "content": {
488
573
  "application/json": {
489
574
  "schema": {
@@ -494,6 +579,11 @@
494
579
  },
495
580
  "201": {
496
581
  "description": "paths.produtosById.put.responses.201.description",
582
+ "headers": {
583
+ "X-ServeRest-Stages": {
584
+ "$ref": "#/components/headers/XServeRestStages"
585
+ }
586
+ },
497
587
  "content": {
498
588
  "application/json": {
499
589
  "schema": {
@@ -538,6 +628,11 @@
538
628
  "responses": {
539
629
  "200": {
540
630
  "description": "paths.carrinhos.get.responses.200.description",
631
+ "headers": {
632
+ "X-ServeRest-Stages": {
633
+ "$ref": "#/components/headers/XServeRestStages"
634
+ }
635
+ },
541
636
  "content": {
542
637
  "application/json": {
543
638
  "schema": {
@@ -565,6 +660,11 @@
565
660
  "responses": {
566
661
  "201": {
567
662
  "description": "paths.carrinhos.post.responses.201.description",
663
+ "headers": {
664
+ "X-ServeRest-Stages": {
665
+ "$ref": "#/components/headers/XServeRestStages"
666
+ }
667
+ },
568
668
  "content": {
569
669
  "application/json": {
570
670
  "schema": {
@@ -575,6 +675,11 @@
575
675
  },
576
676
  "400": {
577
677
  "description": "paths.carrinhos.post.responses.400.description",
678
+ "headers": {
679
+ "X-ServeRest-Stages": {
680
+ "$ref": "#/components/headers/XServeRestStages"
681
+ }
682
+ },
578
683
  "content": {
579
684
  "application/json": {
580
685
  "schema": {
@@ -616,6 +721,11 @@
616
721
  "responses": {
617
722
  "200": {
618
723
  "description": "paths.carrinhosById.get.responses.200.description",
724
+ "headers": {
725
+ "X-ServeRest-Stages": {
726
+ "$ref": "#/components/headers/XServeRestStages"
727
+ }
728
+ },
619
729
  "content": {
620
730
  "application/json": {
621
731
  "schema": {
@@ -626,6 +736,11 @@
626
736
  },
627
737
  "400": {
628
738
  "description": "paths.carrinhosById.get.responses.400.description",
739
+ "headers": {
740
+ "X-ServeRest-Stages": {
741
+ "$ref": "#/components/headers/XServeRestStages"
742
+ }
743
+ },
629
744
  "content": {
630
745
  "application/json": {
631
746
  "schema": {
@@ -658,6 +773,11 @@
658
773
  "responses": {
659
774
  "200": {
660
775
  "description": "paths.carrinhosConcluirCompra.delete.responses.200.description",
776
+ "headers": {
777
+ "X-ServeRest-Stages": {
778
+ "$ref": "#/components/headers/XServeRestStages"
779
+ }
780
+ },
661
781
  "content": {
662
782
  "application/json": {
663
783
  "schema": {
@@ -693,6 +813,11 @@
693
813
  "responses": {
694
814
  "200": {
695
815
  "description": "paths.carrinhosCancelarCompra.delete.responses.200.description",
816
+ "headers": {
817
+ "X-ServeRest-Stages": {
818
+ "$ref": "#/components/headers/XServeRestStages"
819
+ }
820
+ },
696
821
  "content": {
697
822
  "application/json": {
698
823
  "schema": {
@@ -894,6 +1019,11 @@
894
1019
  "responses": {
895
1020
  "EmailJaUtilizado": {
896
1021
  "description": "components.responses.EmailJaUtilizado.description",
1022
+ "headers": {
1023
+ "X-ServeRest-Stages": {
1024
+ "$ref": "#/components/headers/XServeRestStages"
1025
+ }
1026
+ },
897
1027
  "content": {
898
1028
  "application/json": {
899
1029
  "schema": {
@@ -904,6 +1034,11 @@
904
1034
  },
905
1035
  "ProdutoNomeDuplicado": {
906
1036
  "description": "components.responses.ProdutoNomeDuplicado.description",
1037
+ "headers": {
1038
+ "X-ServeRest-Stages": {
1039
+ "$ref": "#/components/headers/XServeRestStages"
1040
+ }
1041
+ },
907
1042
  "content": {
908
1043
  "application/json": {
909
1044
  "schema": {
@@ -914,6 +1049,11 @@
914
1049
  },
915
1050
  "TokenInvalido": {
916
1051
  "description": "components.responses.TokenInvalido.description",
1052
+ "headers": {
1053
+ "X-ServeRest-Stages": {
1054
+ "$ref": "#/components/headers/XServeRestStages"
1055
+ }
1056
+ },
917
1057
  "content": {
918
1058
  "application/json": {
919
1059
  "schema": {
@@ -924,6 +1064,11 @@
924
1064
  },
925
1065
  "RotaParaAdministradores": {
926
1066
  "description": "components.responses.RotaParaAdministradores.description",
1067
+ "headers": {
1068
+ "X-ServeRest-Stages": {
1069
+ "$ref": "#/components/headers/XServeRestStages"
1070
+ }
1071
+ },
927
1072
  "content": {
928
1073
  "application/json": {
929
1074
  "schema": {
@@ -933,6 +1078,15 @@
933
1078
  }
934
1079
  }
935
1080
  },
1081
+ "headers": {
1082
+ "XServeRestStages": {
1083
+ "description": "components.headers.XServeRestStages.description",
1084
+ "schema": {
1085
+ "type": "string",
1086
+ "example": "body;st=ok;dur=0.21, rate_limit;st=skipped, auth;st=failed;dur=0.05;reason=token_missing, schema;st=not_run, rules;st=not_run, write;st=not_run"
1087
+ }
1088
+ }
1089
+ },
936
1090
  "securitySchemes": {
937
1091
  "ApiKeyAuth": {
938
1092
  "type": "apiKey",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "serverest",
3
- "version": "3.2.2",
3
+ "version": "3.3.0-beta.2",
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",
@@ -66,6 +66,7 @@
66
66
  "moesif-nodejs": "^3.11.0",
67
67
  "morgan": "^1.10.1",
68
68
  "nedb-promises": "^6.2.3",
69
+ "on-headers": "^1.1.0",
69
70
  "open": "^11.0.0",
70
71
  "rate-limiter-flexible": "^9.1.0",
71
72
  "swagger-ui-express": "^5.0.1",
package/src/app.js CHANGED
@@ -26,6 +26,7 @@ const moesifMiddleware = require('./middlewares/moesif-monitor-middleware')
26
26
  const { version } = require('../package.json')
27
27
  const swaggerDocument = require('../docs/swagger.json')
28
28
  const rateLimiter = require('./middlewares/rate-limiter')
29
+ const { iniciar, etapa } = require('./utils/etapas')
29
30
  const { fetchLatestRelease } = require('./utils/github-release')
30
31
  const { buildCustomJsStr, customCss } = require('./swagger')
31
32
  const { localizeSwaggerDocument } = require('./swagger/translations')
@@ -55,13 +56,15 @@ app.set('json spaces', 4)
55
56
  // o Express passaria a ler o primeiro endereço do X-Forwarded-For, que o próprio cliente
56
57
  // pode forjar, então o número de saltos precisa ser explícito.
57
58
  app.set('trust proxy', 1)
58
- app.use(express.json())
59
- app.use(express.urlencoded({ extended: false }))
59
+ app.use(['/login', '/usuarios', '/produtos', '/carrinhos'], iniciar)
60
+ const parsersDoBody = express.Router()
61
+ parsersDoBody.use(express.json(), express.urlencoded({ extended: false }))
62
+ app.use(etapa('body', parsersDoBody))
60
63
  app.use(queryParser())
61
64
  app.use(timeout())
62
- app.use(cors())
65
+ app.use(cors({ exposedHeaders: ['X-ServeRest-Stages'] }))
63
66
  app.use(moesifMiddleware)
64
- app.use(rateLimiter)
67
+ app.use(etapa('rate_limit', rateLimiter))
65
68
 
66
69
  app.disable('etag')
67
70
 
@@ -119,7 +122,17 @@ if (!ehAmbienteDeTestes) {
119
122
  const uiOptionsBase = {
120
123
  customSiteTitle: 'ServeRest',
121
124
  customfavIcon: '/favicon.ico',
122
- customCss
125
+ customCss,
126
+ swaggerOptions: {
127
+ // O swagger-ui-express serializa estas funções pelo código-fonte e elas só executam no navegador.
128
+ // A guarda evita página em branco quando o service worker serve um init mais novo que a página.
129
+ plugins: [
130
+ /* istanbul ignore next */
131
+ function () { return window.SwaggerUIBundle.plugins.DownloadUrl.apply(this, arguments) },
132
+ /* istanbul ignore next */
133
+ function () { return window.serverestFlowPanelPlugin ? window.serverestFlowPanelPlugin.apply(this, arguments) : {} }
134
+ ]
135
+ }
123
136
  }
124
137
 
125
138
  app.use('/', swaggerUi.serve)
@@ -18,7 +18,11 @@ exports.getOne = async (req, res) => {
18
18
  }
19
19
 
20
20
  exports.post = async (req, res) => {
21
+ const { etapas } = res.locals
22
+ etapas.regras('single_cart', 'no_duplicate_product', 'product_exists', 'enough_stock')
23
+
21
24
  const { idUsuario, possuiCarrinho } = await service.usuarioJaPossuiCarrinho(req.headers.authorization)
25
+ etapas.regra('single_cart', !possuiCarrinho)
22
26
  if (possuiCarrinho) {
23
27
  return res.status(400).send({ message: constant.LIMIT_JUST_ONE_CART })
24
28
  }
@@ -26,14 +30,26 @@ exports.post = async (req, res) => {
26
30
  const { produtos } = req.body
27
31
 
28
32
  const idProdutosDuplicados = service.extrairProdutosDuplicados(produtos)
33
+ etapas.regra('no_duplicate_product', !idProdutosDuplicados.length)
29
34
  if (idProdutosDuplicados.length) {
30
35
  return res.status(400).send({ message: constant.CART_WITH_DUPLICATE_PRODUCT, idProdutosDuplicados })
31
36
  }
32
37
 
33
38
  const { produtosComPrecoUnitario, error } = await service.getProdutosComPrecoUnitarioOuErro(produtos)
34
39
  if (error) {
40
+ // As duas regras são avaliadas item a item e param no primeiro item que falha.
41
+ if (error.message === constant.PRODUCT_NOT_FOUND) {
42
+ etapas.regra('product_exists', false, { item: error.index })
43
+ } else {
44
+ etapas.regra('product_exists', true)
45
+ etapas.regra('enough_stock', false, { item: error.index })
46
+ }
35
47
  return res.status(error.statusCode).send({ message: error.message, item: error.item })
36
48
  }
49
+ etapas.regra('product_exists', true)
50
+ etapas.regra('enough_stock', true)
51
+
52
+ etapas.abrir('write')
37
53
  const precoTotal = await service.precoTotal(produtosComPrecoUnitario)
38
54
  const quantidadeTotal = await service.quantidadeTotal(produtosComPrecoUnitario)
39
55
 
@@ -1,41 +1,45 @@
1
1
  'use strict'
2
2
 
3
+ const { TokenExpiredError } = require('jsonwebtoken')
4
+
3
5
  const authService = require('../services/auth-service')
4
6
  const constant = require('../utils/constants')
5
7
  const usuariosService = require('../services/usuarios-service')
8
+ const { etapa } = require('../utils/etapas')
6
9
 
7
- exports.checkAdm = async (req, res, next) => {
8
- if (!await tokenValido(req.headers)) {
10
+ exports.checkAdm = etapa('auth', async (req, res, next) => {
11
+ const motivo = await motivoDoTokenInvalido(req.headers)
12
+ if (motivo) {
13
+ res.locals.etapas.detalhar({ reason: motivo })
9
14
  return res.status(401).send({ message: constant.INVALID_TOKEN })
10
15
  }
11
16
  const tokenDecodificado = authService.verifyToken(req.headers.authorization)
12
17
  if (!await usuariosService.usuarioEhAdministrador(tokenDecodificado)) {
18
+ res.locals.etapas.detalhar({ reason: 'not_admin' })
13
19
  return res.status(403).send({ message: constant.REQUIRED_ADMIN })
14
20
  }
15
21
  next()
16
- }
22
+ })
17
23
 
18
- exports.checkToken = async (req, res, next) => {
19
- if (!await tokenValido(req.headers)) {
24
+ exports.checkToken = etapa('auth', async (req, res, next) => {
25
+ const motivo = await motivoDoTokenInvalido(req.headers)
26
+ if (motivo) {
27
+ res.locals.etapas.detalhar({ reason: motivo })
20
28
  return res.status(401).send({ message: constant.INVALID_TOKEN })
21
29
  }
22
30
  next()
23
- }
31
+ })
24
32
 
25
- async function tokenValido ({ authorization }) {
26
- if (authorization === undefined) return false
33
+ async function motivoDoTokenInvalido ({ authorization }) {
34
+ if (authorization === undefined) return 'token_missing'
27
35
 
28
- const semBearer = authorization.split(' ')[0] !== 'Bearer'
29
- const semToken = authorization.split(' ')[1] === undefined
30
-
31
- if (semBearer || semToken) {
32
- return false
33
- }
36
+ const [tipo, token] = authorization.split(' ')
37
+ if (tipo !== 'Bearer' || !token) return 'bad_format'
34
38
 
35
39
  const tokenDecodificado = authService.verifyToken(authorization)
36
- if (tokenDecodificado.email === undefined) {
37
- return false
38
- }
40
+ if (tokenDecodificado instanceof TokenExpiredError) return 'token_expired'
41
+ if (tokenDecodificado.email === undefined) return 'token_invalid'
39
42
 
40
- return usuariosService.existeUsuario({ email: tokenDecodificado.email, password: tokenDecodificado.password })
43
+ const usuario = await usuariosService.existeUsuario({ email: tokenDecodificado.email, password: tokenDecodificado.password })
44
+ if (!usuario) return 'user_not_found'
41
45
  }
@@ -24,6 +24,7 @@ const limiteGlobal = new RateLimiterMemory({
24
24
 
25
25
  module.exports = async (req, res, next) => {
26
26
  if (aplicacaoExecutandoLocalmente() || ehAmbienteDeTestes) {
27
+ res.locals.etapas?.ignorar()
27
28
  return next()
28
29
  }
29
30
 
@@ -6,12 +6,13 @@ const authentication = require('../middlewares/authentication-middleware')
6
6
  const controller = require('../controllers/carrinhos-controller')
7
7
  const model = require('../models/carrinhos-model')
8
8
  const validateSchema = require('../services/validateSchema-service')
9
+ const { etapa, plano } = require('../utils/etapas')
9
10
 
10
11
  const router = express.Router()
11
- router.get('/', validateSchema(model.schemaGet), controller.get)
12
- router.get('/:id', validateSchema(model.schemaGetOne), controller.getOne)
13
- router.post('/', authentication.checkToken, validateSchema(model.schemaPost), controller.post)
14
- router.delete('/concluir-compra', authentication.checkToken, controller.concluirCompra)
15
- router.delete('/cancelar-compra', authentication.checkToken, controller.cancelarCompra)
12
+ router.get('/', plano('schema', 'read'), validateSchema(model.schemaGet), etapa('read', controller.get))
13
+ router.get('/:id', plano('schema', 'read'), validateSchema(model.schemaGetOne), etapa('read', controller.getOne))
14
+ router.post('/', plano('auth', 'schema', 'rules', 'write'), authentication.checkToken, validateSchema(model.schemaPost), etapa('rules', controller.post))
15
+ router.delete('/concluir-compra', plano('auth', 'rules'), authentication.checkToken, etapa('rules', controller.concluirCompra))
16
+ router.delete('/cancelar-compra', plano('auth', 'rules'), authentication.checkToken, etapa('rules', controller.cancelarCompra))
16
17
 
17
18
  module.exports = router
@@ -5,8 +5,9 @@ const express = require('express')
5
5
  const controller = require('../controllers/login-controller')
6
6
  const model = require('../models/login-model')
7
7
  const validateSchema = require('../services/validateSchema-service')
8
+ const { etapa, plano } = require('../utils/etapas')
8
9
 
9
10
  const router = express.Router()
10
- router.post('/', validateSchema(model.schemaPost), controller.post)
11
+ router.post('/', plano('schema', 'rules'), validateSchema(model.schemaPost), etapa('rules', controller.post))
11
12
 
12
13
  module.exports = router
@@ -6,12 +6,13 @@ const authentication = require('../middlewares/authentication-middleware')
6
6
  const controller = require('../controllers/produtos-controller')
7
7
  const model = require('../models/produtos-model')
8
8
  const validateSchema = require('../services/validateSchema-service')
9
+ const { etapa, plano } = require('../utils/etapas')
9
10
 
10
11
  const router = express.Router()
11
- router.get('/', validateSchema(model.schemaGet), controller.get)
12
- router.get('/:id', validateSchema(model.schemaGetOne), controller.getOne)
13
- router.post('/', authentication.checkAdm, validateSchema(model.schemaPost), controller.post)
14
- router.delete('/:id', authentication.checkAdm, validateSchema(model.schemaDelete), controller.delete)
15
- router.put('/:id', authentication.checkAdm, validateSchema(model.schemaPut), controller.put)
12
+ router.get('/', plano('schema', 'read'), validateSchema(model.schemaGet), etapa('read', controller.get))
13
+ router.get('/:id', plano('schema', 'read'), validateSchema(model.schemaGetOne), etapa('read', controller.getOne))
14
+ router.post('/', plano('auth', 'schema', 'rules'), authentication.checkAdm, validateSchema(model.schemaPost), etapa('rules', controller.post))
15
+ router.delete('/:id', plano('auth', 'schema', 'rules'), authentication.checkAdm, validateSchema(model.schemaDelete), etapa('rules', controller.delete))
16
+ router.put('/:id', plano('auth', 'schema', 'rules'), authentication.checkAdm, validateSchema(model.schemaPut), etapa('rules', controller.put))
16
17
 
17
18
  module.exports = router
@@ -5,12 +5,13 @@ const express = require('express')
5
5
  const controller = require('../controllers/usuarios-controller')
6
6
  const model = require('../models/usuarios-model')
7
7
  const validateSchema = require('../services/validateSchema-service')
8
+ const { etapa, plano } = require('../utils/etapas')
8
9
 
9
10
  const router = express.Router()
10
- router.get('/', validateSchema(model.schemaGet), controller.get)
11
- router.get('/:id', validateSchema(model.schemaGetOne), controller.getOne)
12
- router.post('/', validateSchema(model.schemaPost), controller.post)
13
- router.delete('/:id', validateSchema(model.schemaDelete), controller.delete)
14
- router.put('/:id', validateSchema(model.schemaPut), controller.put)
11
+ router.get('/', plano('schema', 'read'), validateSchema(model.schemaGet), etapa('read', controller.get))
12
+ router.get('/:id', plano('schema', 'read'), validateSchema(model.schemaGetOne), etapa('read', controller.getOne))
13
+ router.post('/', plano('schema', 'rules'), validateSchema(model.schemaPost), etapa('rules', controller.post))
14
+ router.delete('/:id', plano('schema', 'rules'), validateSchema(model.schemaDelete), etapa('rules', controller.delete))
15
+ router.put('/:id', plano('schema', 'rules'), validateSchema(model.schemaPut), etapa('rules', controller.put))
15
16
 
16
17
  module.exports = router