diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..95c5cc4
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,33 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+.env.example
+.env
+.env.local
+.env.development.local
+/.env.example
\ No newline at end of file
diff --git a/README.md b/README.md
index b952191..b4a3a56 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,5 @@
-
Material referente as entregas dos desafios no curso de Back-End na Coderhouse Brasil
-
+Descrição:
-
- Aulas com entrega:
-
-Aula 2 ( Classes com ECMAScript e ECMAScript avançado )
-Aula 4 ( Gestão de arquivos em JavaScript )
-Aula 6 ( Servidor com Express )
-Aula 8 ( Controllers e Middlewares )
-Aula 10 ( Websockets )
-Aula 15 ( Prática integradora )
-Aula 17 ( Mongo Avançado II )
-Aula 19 ( Cookies, Sessions & Storage II )
-Aula 21 ( Estratégia de autenticação de terceiros + JWT )
-Aula 27 ( Reestruturação do nosso servidor )
-Aula 30 ( Entrega Parcial 3 do Projeto Final )
-Aula 32 ( Mocking e tratamento de erros )
-Aula 34 ( Implementação do registrador )
-
-
-
-Acesse as aulas pelas branches
+
+
06/05/2025 Desafio ( Documentar API ) da Aula 39 ( Documentação da API REST ) do curso de Back-end na CoderHouse; Turma #63515 de 2024/25.
+
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ff436c8
--- /dev/null
+++ b/package.json
@@ -0,0 +1,7 @@
+{
+ "dependencies": {
+ "express": "^5.1.0",
+ "swagger-ui-express": "^5.0.1",
+ "swagger-jsdoc": "^6.2.8"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..edadaa5
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,827 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ express:
+ specifier: ^5.1.0
+ version: 5.1.0
+ swagger-jsdoc:
+ specifier: ^6.2.8
+ version: 6.2.8(openapi-types@12.1.3)
+ swagger-ui-express:
+ specifier: ^5.0.1
+ version: 5.0.1(express@5.1.0)
+
+packages:
+
+ '@apidevtools/json-schema-ref-parser@9.1.2':
+ resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==}
+
+ '@apidevtools/openapi-schemas@2.1.0':
+ resolution: {integrity: sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==}
+ engines: {node: '>=10'}
+
+ '@apidevtools/swagger-methods@3.0.2':
+ resolution: {integrity: sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==}
+
+ '@apidevtools/swagger-parser@10.0.3':
+ resolution: {integrity: sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==}
+ peerDependencies:
+ openapi-types: '>=7'
+
+ '@jsdevtools/ono@7.1.3':
+ resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==}
+
+ '@scarf/scarf@1.4.0':
+ resolution: {integrity: sha512-xxeapPiUXdZAE3che6f3xogoJPeZgig6omHEy1rIY5WVsB3H2BHNnZH+gHG6x91SCWyQCzWGsuL2Hh3ClO5/qQ==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ accepts@2.0.0:
+ resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
+ engines: {node: '>= 0.6'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ body-parser@2.2.0:
+ resolution: {integrity: sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==}
+ engines: {node: '>=18'}
+
+ brace-expansion@1.1.12:
+ resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==}
+
+ bytes@3.1.2:
+ resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+ engines: {node: '>= 0.8'}
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ call-me-maybe@1.0.2:
+ resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
+
+ commander@6.2.0:
+ resolution: {integrity: sha512-zP4jEKbe8SHzKJYQmq8Y9gYjtO/POJLgIdKgV7B9qNmABVFVc+ctqSX6iXh4mCpJfRBOabiZ2YKPg8ciDw6C+Q==}
+ engines: {node: '>= 6'}
+
+ commander@9.5.0:
+ resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+ engines: {node: ^12.20.0 || >=14}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ content-disposition@1.0.0:
+ resolution: {integrity: sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==}
+ engines: {node: '>= 0.6'}
+
+ content-type@1.0.5:
+ resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+ engines: {node: '>= 0.6'}
+
+ cookie-signature@1.2.2:
+ resolution: {integrity: sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==}
+ engines: {node: '>=6.6.0'}
+
+ cookie@0.7.2:
+ resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
+ engines: {node: '>= 0.6'}
+
+ debug@4.4.1:
+ resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ depd@2.0.0:
+ resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+ engines: {node: '>= 0.8'}
+
+ doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ ee-first@1.1.1:
+ resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+ encodeurl@2.0.0:
+ resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+ engines: {node: '>= 0.8'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ escape-html@1.0.3:
+ resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ etag@1.8.1:
+ resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+ engines: {node: '>= 0.6'}
+
+ express@5.1.0:
+ resolution: {integrity: sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==}
+ engines: {node: '>= 18'}
+
+ finalhandler@2.1.0:
+ resolution: {integrity: sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==}
+ engines: {node: '>= 0.8'}
+
+ forwarded@0.2.0:
+ resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+ engines: {node: '>= 0.6'}
+
+ fresh@2.0.0:
+ resolution: {integrity: sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==}
+ engines: {node: '>= 0.8'}
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ glob@7.1.6:
+ resolution: {integrity: sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.2:
+ resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+ engines: {node: '>= 0.4'}
+
+ http-errors@2.0.0:
+ resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+ engines: {node: '>= 0.8'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ ipaddr.js@1.9.1:
+ resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+ engines: {node: '>= 0.10'}
+
+ is-promise@4.0.0:
+ resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ lodash.get@4.4.2:
+ resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
+ deprecated: This package is deprecated. Use the optional chaining (?.) operator instead.
+
+ lodash.isequal@4.5.0:
+ resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==}
+ deprecated: This package is deprecated. Use require('node:util').isDeepStrictEqual instead.
+
+ lodash.mergewith@4.6.2:
+ resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==}
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ media-typer@1.1.0:
+ resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
+ engines: {node: '>= 0.8'}
+
+ merge-descriptors@2.0.0:
+ resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
+ engines: {node: '>=18'}
+
+ mime-db@1.54.0:
+ resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==}
+ engines: {node: '>= 0.6'}
+
+ mime-types@3.0.1:
+ resolution: {integrity: sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==}
+ engines: {node: '>= 0.6'}
+
+ minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ on-finished@2.4.1:
+ resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+ engines: {node: '>= 0.8'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ openapi-types@12.1.3:
+ resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
+
+ parseurl@1.3.3:
+ resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+ engines: {node: '>= 0.8'}
+
+ path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ path-to-regexp@8.2.0:
+ resolution: {integrity: sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==}
+ engines: {node: '>=16'}
+
+ proxy-addr@2.0.7:
+ resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+ engines: {node: '>= 0.10'}
+
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
+ range-parser@1.2.1:
+ resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+ engines: {node: '>= 0.6'}
+
+ raw-body@3.0.0:
+ resolution: {integrity: sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==}
+ engines: {node: '>= 0.8'}
+
+ router@2.2.0:
+ resolution: {integrity: sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==}
+ engines: {node: '>= 18'}
+
+ safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ send@1.2.0:
+ resolution: {integrity: sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==}
+ engines: {node: '>= 18'}
+
+ serve-static@2.2.0:
+ resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
+ engines: {node: '>= 18'}
+
+ setprototypeof@1.2.0:
+ resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+ side-channel-list@1.0.0:
+ resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.0:
+ resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+ engines: {node: '>= 0.4'}
+
+ statuses@2.0.1:
+ resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+ engines: {node: '>= 0.8'}
+
+ statuses@2.0.2:
+ resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
+ engines: {node: '>= 0.8'}
+
+ swagger-jsdoc@6.2.8:
+ resolution: {integrity: sha512-VPvil1+JRpmJ55CgAtn8DIcpBs0bL5L3q5bVQvF4tAW/k/9JYSj7dCpaYCAv5rufe0vcCbBRQXGvzpkWjvLklQ==}
+ engines: {node: '>=12.0.0'}
+ hasBin: true
+
+ swagger-parser@10.0.3:
+ resolution: {integrity: sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==}
+ engines: {node: '>=10'}
+
+ swagger-ui-dist@5.25.2:
+ resolution: {integrity: sha512-V4JyoygUe5nCbn7bAD0fVKSC0yNcL3ROIQtGC7M0NATKuyosCSmMU6T0yDZIIuGpSxjsjZh/D2Ejb8lnF2jjxw==}
+
+ swagger-ui-express@5.0.1:
+ resolution: {integrity: sha512-SrNU3RiBGTLLmFU8GIJdOdanJTl4TOmT27tt3bWWHppqYmAZ6IDuEuBvMU6nZq0zLEe6b/1rACXCgLZqO6ZfrA==}
+ engines: {node: '>= v0.10.32'}
+ peerDependencies:
+ express: '>=4.0.0 || >=5.0.0-beta'
+
+ toidentifier@1.0.1:
+ resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+ engines: {node: '>=0.6'}
+
+ type-is@2.0.1:
+ resolution: {integrity: sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==}
+ engines: {node: '>= 0.6'}
+
+ unpipe@1.0.0:
+ resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+ engines: {node: '>= 0.8'}
+
+ validator@13.15.15:
+ resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==}
+ engines: {node: '>= 0.10'}
+
+ vary@1.1.2:
+ resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+ engines: {node: '>= 0.8'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ yaml@2.0.0-1:
+ resolution: {integrity: sha512-W7h5dEhywMKenDJh2iX/LABkbFnBxasD27oyXWDS/feDsxiw0dD5ncXdYXgkvAsXIY2MpW/ZKkr9IU30DBdMNQ==}
+ engines: {node: '>= 6'}
+
+ z-schema@5.0.5:
+ resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==}
+ engines: {node: '>=8.0.0'}
+ hasBin: true
+
+snapshots:
+
+ '@apidevtools/json-schema-ref-parser@9.1.2':
+ dependencies:
+ '@jsdevtools/ono': 7.1.3
+ '@types/json-schema': 7.0.15
+ call-me-maybe: 1.0.2
+ js-yaml: 4.1.0
+
+ '@apidevtools/openapi-schemas@2.1.0': {}
+
+ '@apidevtools/swagger-methods@3.0.2': {}
+
+ '@apidevtools/swagger-parser@10.0.3(openapi-types@12.1.3)':
+ dependencies:
+ '@apidevtools/json-schema-ref-parser': 9.1.2
+ '@apidevtools/openapi-schemas': 2.1.0
+ '@apidevtools/swagger-methods': 3.0.2
+ '@jsdevtools/ono': 7.1.3
+ call-me-maybe: 1.0.2
+ openapi-types: 12.1.3
+ z-schema: 5.0.5
+
+ '@jsdevtools/ono@7.1.3': {}
+
+ '@scarf/scarf@1.4.0': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ accepts@2.0.0:
+ dependencies:
+ mime-types: 3.0.1
+ negotiator: 1.0.0
+
+ argparse@2.0.1: {}
+
+ balanced-match@1.0.2: {}
+
+ body-parser@2.2.0:
+ dependencies:
+ bytes: 3.1.2
+ content-type: 1.0.5
+ debug: 4.4.1
+ http-errors: 2.0.0
+ iconv-lite: 0.6.3
+ on-finished: 2.4.1
+ qs: 6.14.0
+ raw-body: 3.0.0
+ type-is: 2.0.1
+ transitivePeerDependencies:
+ - supports-color
+
+ brace-expansion@1.1.12:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ bytes@3.1.2: {}
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ call-me-maybe@1.0.2: {}
+
+ commander@6.2.0: {}
+
+ commander@9.5.0:
+ optional: true
+
+ concat-map@0.0.1: {}
+
+ content-disposition@1.0.0:
+ dependencies:
+ safe-buffer: 5.2.1
+
+ content-type@1.0.5: {}
+
+ cookie-signature@1.2.2: {}
+
+ cookie@0.7.2: {}
+
+ debug@4.4.1:
+ dependencies:
+ ms: 2.1.3
+
+ depd@2.0.0: {}
+
+ doctrine@3.0.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ ee-first@1.1.1: {}
+
+ encodeurl@2.0.0: {}
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-object-atoms@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+
+ escape-html@1.0.3: {}
+
+ esutils@2.0.3: {}
+
+ etag@1.8.1: {}
+
+ express@5.1.0:
+ dependencies:
+ accepts: 2.0.0
+ body-parser: 2.2.0
+ content-disposition: 1.0.0
+ content-type: 1.0.5
+ cookie: 0.7.2
+ cookie-signature: 1.2.2
+ debug: 4.4.1
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ finalhandler: 2.1.0
+ fresh: 2.0.0
+ http-errors: 2.0.0
+ merge-descriptors: 2.0.0
+ mime-types: 3.0.1
+ on-finished: 2.4.1
+ once: 1.4.0
+ parseurl: 1.3.3
+ proxy-addr: 2.0.7
+ qs: 6.14.0
+ range-parser: 1.2.1
+ router: 2.2.0
+ send: 1.2.0
+ serve-static: 2.2.0
+ statuses: 2.0.2
+ type-is: 2.0.1
+ vary: 1.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ finalhandler@2.1.0:
+ dependencies:
+ debug: 4.4.1
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ on-finished: 2.4.1
+ parseurl: 1.3.3
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ forwarded@0.2.0: {}
+
+ fresh@2.0.0: {}
+
+ fs.realpath@1.0.0: {}
+
+ function-bind@1.1.2: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ glob@7.1.6:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ gopd@1.2.0: {}
+
+ has-symbols@1.1.0: {}
+
+ hasown@2.0.2:
+ dependencies:
+ function-bind: 1.1.2
+
+ http-errors@2.0.0:
+ dependencies:
+ depd: 2.0.0
+ inherits: 2.0.4
+ setprototypeof: 1.2.0
+ statuses: 2.0.1
+ toidentifier: 1.0.1
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ inherits@2.0.4: {}
+
+ ipaddr.js@1.9.1: {}
+
+ is-promise@4.0.0: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ lodash.get@4.4.2: {}
+
+ lodash.isequal@4.5.0: {}
+
+ lodash.mergewith@4.6.2: {}
+
+ math-intrinsics@1.1.0: {}
+
+ media-typer@1.1.0: {}
+
+ merge-descriptors@2.0.0: {}
+
+ mime-db@1.54.0: {}
+
+ mime-types@3.0.1:
+ dependencies:
+ mime-db: 1.54.0
+
+ minimatch@3.1.2:
+ dependencies:
+ brace-expansion: 1.1.12
+
+ ms@2.1.3: {}
+
+ negotiator@1.0.0: {}
+
+ object-inspect@1.13.4: {}
+
+ on-finished@2.4.1:
+ dependencies:
+ ee-first: 1.1.1
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ openapi-types@12.1.3: {}
+
+ parseurl@1.3.3: {}
+
+ path-is-absolute@1.0.1: {}
+
+ path-to-regexp@8.2.0: {}
+
+ proxy-addr@2.0.7:
+ dependencies:
+ forwarded: 0.2.0
+ ipaddr.js: 1.9.1
+
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
+ range-parser@1.2.1: {}
+
+ raw-body@3.0.0:
+ dependencies:
+ bytes: 3.1.2
+ http-errors: 2.0.0
+ iconv-lite: 0.6.3
+ unpipe: 1.0.0
+
+ router@2.2.0:
+ dependencies:
+ debug: 4.4.1
+ depd: 2.0.0
+ is-promise: 4.0.0
+ parseurl: 1.3.3
+ path-to-regexp: 8.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ safe-buffer@5.2.1: {}
+
+ safer-buffer@2.1.2: {}
+
+ send@1.2.0:
+ dependencies:
+ debug: 4.4.1
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ etag: 1.8.1
+ fresh: 2.0.0
+ http-errors: 2.0.0
+ mime-types: 3.0.1
+ ms: 2.1.3
+ on-finished: 2.4.1
+ range-parser: 1.2.1
+ statuses: 2.0.2
+ transitivePeerDependencies:
+ - supports-color
+
+ serve-static@2.2.0:
+ dependencies:
+ encodeurl: 2.0.0
+ escape-html: 1.0.3
+ parseurl: 1.3.3
+ send: 1.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ setprototypeof@1.2.0: {}
+
+ side-channel-list@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.0
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ statuses@2.0.1: {}
+
+ statuses@2.0.2: {}
+
+ swagger-jsdoc@6.2.8(openapi-types@12.1.3):
+ dependencies:
+ commander: 6.2.0
+ doctrine: 3.0.0
+ glob: 7.1.6
+ lodash.mergewith: 4.6.2
+ swagger-parser: 10.0.3(openapi-types@12.1.3)
+ yaml: 2.0.0-1
+ transitivePeerDependencies:
+ - openapi-types
+
+ swagger-parser@10.0.3(openapi-types@12.1.3):
+ dependencies:
+ '@apidevtools/swagger-parser': 10.0.3(openapi-types@12.1.3)
+ transitivePeerDependencies:
+ - openapi-types
+
+ swagger-ui-dist@5.25.2:
+ dependencies:
+ '@scarf/scarf': 1.4.0
+
+ swagger-ui-express@5.0.1(express@5.1.0):
+ dependencies:
+ express: 5.1.0
+ swagger-ui-dist: 5.25.2
+
+ toidentifier@1.0.1: {}
+
+ type-is@2.0.1:
+ dependencies:
+ content-type: 1.0.5
+ media-typer: 1.1.0
+ mime-types: 3.0.1
+
+ unpipe@1.0.0: {}
+
+ validator@13.15.15: {}
+
+ vary@1.1.2: {}
+
+ wrappy@1.0.2: {}
+
+ yaml@2.0.0-1: {}
+
+ z-schema@5.0.5:
+ dependencies:
+ lodash.get: 4.4.2
+ lodash.isequal: 4.5.0
+ validator: 13.15.15
+ optionalDependencies:
+ commander: 9.5.0
diff --git a/src/app.js b/src/app.js
new file mode 100644
index 0000000..c00f923
--- /dev/null
+++ b/src/app.js
@@ -0,0 +1,27 @@
+const express = require('express');
+const productRoutes = require('./routes/product.routes');
+const cartRoutes = require('./routes/cart.routes');
+const swaggerUi = require('swagger-ui-express');
+const swaggerSpec = require('../swagger');
+
+const app = express();
+const PORT = process.env.PORT || 3000;
+
+app.use(express.json());
+
+//! Rotas da API
+app.use('/api/v1', productRoutes);
+app.use('/api/v1', cartRoutes);
+
+//! Configuração do Swagger UI
+app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec)); // A documentação estará disponível em http://localhost:3000/api-docs
+
+//! Rota de teste simples
+app.get('/', (req, res) => {
+ res.send('API de E-commerce está funcionando! Acesse /api-docs para a documentação.');
+});
+
+app.listen(PORT, () => {
+ console.log(`Servidor rodando na porta ${PORT}`);
+ console.log(`Documentação Swagger disponível em http://localhost:${PORT}/api-docs`);
+});
\ No newline at end of file
diff --git a/src/routes/cart.routes.js b/src/routes/cart.routes.js
new file mode 100644
index 0000000..64063b9
--- /dev/null
+++ b/src/routes/cart.routes.js
@@ -0,0 +1,281 @@
+const express = require('express');
+const router = express.Router();
+
+// Simulação de banco de dados de carrinhos
+let carts = {}; // { userId: { userId: '...', items: [{ productId, name, quantity, price }], totalPrice: ... } }
+
+// Helper para encontrar produto (simulação)
+const findProductById = (id) => {
+ // Em um cenário real, você buscaria isso em um serviço de produtos ou DB
+ const products = [
+ { id: 'prod_001', name: 'Smartphone X', price: 2500.00 },
+ { id: 'prod_002', name: 'Fone de Ouvido Bluetooth', price: 350.00 },
+ { id: 'prod_003', name: 'Tablet Pro', price: 1800.00 }
+ ];
+ return products.find(p => p.id === id);
+};
+
+/**
+ * @swagger
+ * /cart/{userId}:
+ * get:
+ * summary: Retorna o carrinho de compras de um usuário.
+ * description: Permite buscar o carrinho de compras associado a um ID de usuário específico. Se o carrinho não existir, pode ser criado um novo (se a lógica da API permitir) ou retornada uma resposta de "não encontrado".
+ * tags:
+ * - Carrinhos
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: O ID único do usuário.
+ * example: user_abc
+ * responses:
+ * 200:
+ * description: Carrinho retornado com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/Cart'
+ * 404:
+ * description: Carrinho para o usuário não encontrado.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Carrinho para o usuário 'user_xyz' não encontrado.
+ */
+router.get('/cart/:userId', (req, res) => {
+ const { userId } = req.params;
+ const cart = carts[userId];
+ if (cart) {
+ res.json(cart);
+ } else {
+ // Em um cenário real, você pode criar um carrinho vazio aqui ou retornar 404
+ res.status(404).json({ error: `Carrinho para o usuário '${userId}' não encontrado.` });
+ }
+});
+
+/**
+ * @swagger
+ * /cart/{userId}/items:
+ * post:
+ * summary: Adiciona um item ao carrinho de um usuário.
+ * description: Adiciona uma determinada quantidade de um produto ao carrinho de compras do usuário. Se o produto já estiver no carrinho, a quantidade será atualizada.
+ * tags:
+ * - Carrinhos
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: O ID único do usuário.
+ * example: user_abc
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/CartItemInput'
+ * example:
+ * productId: prod_003
+ * quantity: 1
+ * responses:
+ * 200:
+ * description: Item adicionado/atualizado no carrinho com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/Cart'
+ * 400:
+ * description: Dados de entrada inválidos ou quantidade inválida.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Quantidade inválida para o produto 'prod_003'.
+ * 404:
+ * description: Produto não encontrado ou usuário não existe para criar carrinho.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Produto com ID 'prod_999' não encontrado.
+ */
+router.post('/cart/:userId/items', (req, res) => {
+ const { userId } = req.params;
+ const { productId, quantity } = req.body;
+
+ if (!productId || quantity === undefined || quantity <= 0) {
+ return res.status(400).json({ error: 'productId e quantity (maior que zero) são obrigatórios.' });
+ }
+
+ const product = findProductById(productId);
+ if (!product) {
+ return res.status(404).json({ error: `Produto com ID '${productId}' não encontrado.` });
+ }
+
+ if (!carts[userId]) {
+ carts[userId] = { userId: userId, items: [], totalPrice: 0 };
+ }
+
+ const cart = carts[userId];
+ const existingItemIndex = cart.items.findIndex(item => item.productId === productId);
+
+ if (existingItemIndex > -1) {
+ cart.items[existingItemIndex].quantity += quantity;
+ } else {
+ cart.items.push({
+ productId: product.id,
+ name: product.name,
+ quantity: quantity,
+ price: product.price
+ });
+ }
+
+ cart.totalPrice = cart.items.reduce((sum, item) => sum + (item.quantity * item.price), 0);
+ res.status(200).json(cart);
+});
+
+/**
+ * @swagger
+ * /cart/{userId}/items/{productId}:
+ * delete:
+ * summary: Remove um item específico do carrinho de um usuário.
+ * description: Remove um produto completamente do carrinho de compras do usuário.
+ * tags:
+ * - Carrinhos
+ * parameters:
+ * - in: path
+ * name: userId
+ * required: true
+ * schema:
+ * type: string
+ * description: O ID único do usuário.
+ * example: user_abc
+ * - in: path
+ * name: productId
+ * required: true
+ * schema:
+ * type: string
+ * description: O ID único do produto a ser removido.
+ * example: prod_002
+ * responses:
+ * 200:
+ * description: Item removido do carrinho com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * message:
+ * type: string
+ * example: Produto 'prod_002' removido do carrinho do usuário 'user_abc'.
+ * 404:
+ * description: Carrinho ou item não encontrado.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Produto 'prod_999' não encontrado no carrinho do usuário 'user_abc'.
+ */
+router.delete('/cart/:userId/items/:productId', (req, res) => {
+ const { userId, productId } = req.params;
+ const cart = carts[userId];
+
+ if (!cart) {
+ return res.status(404).json({ error: `Carrinho para o usuário '${userId}' não encontrado.` });
+ }
+
+ const initialLength = cart.items.length;
+ cart.items = cart.items.filter(item => item.productId !== productId);
+
+ if (cart.items.length === initialLength) {
+ return res.status(404).json({ error: `Produto '${productId}' não encontrado no carrinho do usuário '${userId}'.` });
+ }
+
+ cart.totalPrice = cart.items.reduce((sum, item) => sum + (item.quantity * item.price), 0);
+ res.status(200).json({ message: `Produto '${productId}' removido do carrinho do usuário '${userId}'.` });
+});
+
+// Definições de Schemas (Modelos de Dados) para reutilização na documentação
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * CartItem:
+ * type: object
+ * required:
+ * - productId
+ * - name
+ * - quantity
+ * - price
+ * properties:
+ * productId:
+ * type: string
+ * description: O ID único do produto.
+ * example: prod_001
+ * name:
+ * type: string
+ * description: O nome do produto.
+ * example: Smartphone X
+ * quantity:
+ * type: integer
+ * description: A quantidade do produto no carrinho.
+ * example: 1
+ * price:
+ * type: number
+ * format: float
+ * description: O preço unitário do produto no momento da adição.
+ * example: 2500.00
+ * Cart:
+ * type: object
+ * required:
+ * - userId
+ * - items
+ * - totalPrice
+ * properties:
+ * userId:
+ * type: string
+ * description: O ID único do usuário.
+ * example: user_abc
+ * items:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/CartItem'
+ * description: Lista de itens no carrinho.
+ * totalPrice:
+ * type: number
+ * format: float
+ * description: O preço total do carrinho.
+ * example: 3200.00
+ * CartItemInput:
+ * type: object
+ * required:
+ * - productId
+ * - quantity
+ * properties:
+ * productId:
+ * type: string
+ * description: O ID do produto a ser adicionado ou atualizado.
+ * example: prod_003
+ * quantity:
+ * type: integer
+ * description: A quantidade do produto a ser adicionada.
+ * example: 1
+ */
+module.exports = router;
\ No newline at end of file
diff --git a/src/routes/product.routes.js b/src/routes/product.routes.js
new file mode 100644
index 0000000..e216dc0
--- /dev/null
+++ b/src/routes/product.routes.js
@@ -0,0 +1,231 @@
+const express = require('express');
+const router = express.Router();
+
+// Simulação de banco de dados de produtos
+let products = [
+ { id: 'prod_001', name: 'Smartphone X', description: 'Última geração de smartphone com câmera de alta resolução.', price: 2500.00, category: 'electronics', stock: 50 },
+ { id: 'prod_002', name: 'Fone de Ouvido Bluetooth', description: 'Fone sem fio com cancelamento de ruído.', price: 350.00, category: 'electronics', stock: 120 }
+];
+
+/**
+ * @swagger
+ * /products:
+ * get:
+ * summary: Retorna uma lista de todos os produtos.
+ * description: Este endpoint permite obter uma lista completa de todos os produtos disponíveis no catálogo da loja, com opção de filtragem por categoria e paginação.
+ * tags:
+ * - Produtos
+ * parameters:
+ * - in: query
+ * name: category
+ * schema:
+ * type: string
+ * description: Filtra produtos por categoria.
+ * example: electronics
+ * - in: query
+ * name: limit
+ * schema:
+ * type: integer
+ * description: Define o número máximo de produtos a serem retornados.
+ * example: 10
+ * responses:
+ * 200:
+ * description: Lista de produtos retornada com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * type: array
+ * items:
+ * $ref: '#/components/schemas/Product'
+ * 500:
+ * description: Erro interno no servidor.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Ocorreu um erro inesperado.
+ */
+router.get('/products', (req, res) => {
+ const { category, limit } = req.query;
+ let filteredProducts = products;
+
+ if (category) {
+ filteredProducts = filteredProducts.filter(p => p.category === category);
+ }
+ if (limit) {
+ filteredProducts = filteredProducts.slice(0, parseInt(limit));
+ }
+ res.json(filteredProducts);
+});
+
+/**
+ * @swagger
+ * /products/{id}:
+ * get:
+ * summary: Retorna os detalhes de um produto específico.
+ * description: Permite buscar os detalhes de um produto usando seu ID único.
+ * tags:
+ * - Produtos
+ * parameters:
+ * - in: path
+ * name: id
+ * required: true
+ * schema:
+ * type: string
+ * description: O ID único do produto.
+ * example: prod_001
+ * responses:
+ * 200:
+ * description: Detalhes do produto retornados com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/Product'
+ * 404:
+ * description: Produto não encontrado.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Produto com o ID 'prod_999' não encontrado.
+ */
+router.get('/products/:id', (req, res) => {
+ const { id } = req.params;
+ const product = products.find(p => p.id === id);
+ if (product) {
+ res.json(product);
+ } else {
+ res.status(404).json({ error: `Produto com o ID '${id}' não encontrado.` });
+ }
+});
+
+/**
+ * @swagger
+ * /products:
+ * post:
+ * summary: Adiciona um novo produto ao catálogo.
+ * description: Cria um novo registro de produto com as informações fornecidas.
+ * tags:
+ * - Produtos
+ * requestBody:
+ * required: true
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/ProductInput'
+ * example:
+ * name: Tablet Pro
+ * description: Tablet de alta performance com tela de 12 polegadas.
+ * price: 1800.00
+ * category: electronics
+ * stock: 30
+ * responses:
+ * 201:
+ * description: Produto criado com sucesso.
+ * content:
+ * application/json:
+ * schema:
+ * $ref: '#/components/schemas/Product'
+ * 400:
+ * description: Dados de entrada inválidos.
+ * content:
+ * application/json:
+ * schema:
+ * type: object
+ * properties:
+ * error:
+ * type: string
+ * example: Nome do produto é obrigatório.
+ */
+router.post('/products', (req, res) => {
+ const { name, description, price, category, stock } = req.body;
+ if (!name || !price || !category || stock === undefined) {
+ return res.status(400).json({ error: 'Nome, preço, categoria e estoque são obrigatórios.' });
+ }
+ const newProduct = {
+ id: `prod_${(products.length + 1).toString().padStart(3, '0')}`,
+ name,
+ description,
+ price,
+ category,
+ stock
+ };
+ products.push(newProduct);
+ res.status(201).json(newProduct);
+});
+
+/**
+ * @swagger
+ * components:
+ * schemas:
+ * Product:
+ * type: object
+ * required:
+ * - id
+ * - name
+ * - price
+ * - category
+ * - stock
+ * properties:
+ * id:
+ * type: string
+ * description: O ID único do produto.
+ * example: prod_001
+ * name:
+ * type: string
+ * description: O nome do produto.
+ * example: Smartphone X
+ * description:
+ * type: string
+ * description: Descrição detalhada do produto.
+ * example: Última geração de smartphone com câmera de alta resolução.
+ * price:
+ * type: number
+ * format: float
+ * description: O preço do produto.
+ * example: 2500.00
+ * category:
+ * type: string
+ * description: A categoria do produto.
+ * example: electronics
+ * stock:
+ * type: integer
+ * description: A quantidade em estoque do produto.
+ * example: 50
+ * ProductInput:
+ * type: object
+ * required:
+ * - name
+ * - price
+ * - category
+ * - stock
+ * properties:
+ * name:
+ * type: string
+ * description: O nome do produto.
+ * example: Tablet Pro
+ * description:
+ * type: string
+ * description: Descrição detalhada do produto.
+ * example: Tablet de alta performance com tela de 12 polegadas.
+ * price:
+ * type: number
+ * format: float
+ * description: O preço do produto.
+ * example: 1800.00
+ * category:
+ * type: string
+ * description: A categoria do produto.
+ * example: electronics
+ * stock:
+ * type: integer
+ * description: A quantidade em estoque do produto.
+ * example: 30
+ */
+module.exports = router;
\ No newline at end of file
diff --git a/swagger.js b/swagger.js
new file mode 100644
index 0000000..04f5523
--- /dev/null
+++ b/swagger.js
@@ -0,0 +1,37 @@
+const swaggerJsdoc = require('swagger-jsdoc');
+
+const options = {
+ definition: {
+ openapi: '3.0.0',
+ info: {
+ title: 'API de E-commerce',
+ version: '1.0.0',
+ description: 'API para gerenciamento de produtos e carrinhos de compras de uma loja virtual. Não documenta sessões.',
+ },
+ servers: [
+ {
+ url: 'http://localhost:3000/api/v1',
+ description: 'Servidor de Desenvolvimento',
+ },
+ ],
+ // components: {
+ // securitySchemes: {
+ // bearerAuth: {
+ // type: 'http',
+ // scheme: 'bearer',
+ // bearerFormat: 'JWT',
+ // }
+ // }
+ // },
+ // security: [
+ // {
+ // bearerAuth: []
+ // }
+ // ]
+ },
+ apis: ['./src/routes/*.js'],
+};
+
+const swaggerSpec = swaggerJsdoc(options);
+
+module.exports = swaggerSpec;
\ No newline at end of file