From a557d8f0581e4f27a91f6c39b4c790fb149b54c8 Mon Sep 17 00:00:00 2001 From: Radu Bogdan Naramzoiu Date: Tue, 8 Oct 2024 17:00:22 +0300 Subject: [PATCH 1/5] First test commit --- routes/api/contacts.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/routes/api/contacts.js b/routes/api/contacts.js index a60ebd69231..46a22a4d464 100644 --- a/routes/api/contacts.js +++ b/routes/api/contacts.js @@ -1,25 +1,25 @@ -const express = require('express') +const express = require("express"); -const router = express.Router() +const router = express.Router(); -router.get('/', async (req, res, next) => { - res.json({ message: 'template message' }) -}) +router.get("/", async (req, res, next) => { + res.json({ message: "Homework in progress !!!" }); +}); -router.get('/:contactId', async (req, res, next) => { - res.json({ message: 'template message' }) -}) +router.get("/:contactId", async (req, res, next) => { + res.json({ message: "template message" }); +}); -router.post('/', async (req, res, next) => { - res.json({ message: 'template message' }) -}) +router.post("/", async (req, res, next) => { + res.json({ message: "template message" }); +}); -router.delete('/:contactId', async (req, res, next) => { - res.json({ message: 'template message' }) -}) +router.delete("/:contactId", async (req, res, next) => { + res.json({ message: "template message" }); +}); -router.put('/:contactId', async (req, res, next) => { - res.json({ message: 'template message' }) -}) +router.put("/:contactId", async (req, res, next) => { + res.json({ message: "template message" }); +}); -module.exports = router +module.exports = router; From 48ab396c3f46e36e58e5b4014fb23e896d1e52b3 Mon Sep 17 00:00:00 2001 From: Radu Bogdan Naramzoiu Date: Tue, 8 Oct 2024 23:33:10 +0300 Subject: [PATCH 2/5] Finished homework2...!!! :))) --- app.js | 31 +-- models/contacts.json | 14 +- package-lock.json | 446 ++++++++++++++++++++++++++--------------- package.json | 1 + routes/api/contacts.js | 203 ++++++++++++++++++- server.js | 6 +- services/actions.js | 232 +++++++++++++++++++++ 7 files changed, 748 insertions(+), 185 deletions(-) create mode 100644 services/actions.js diff --git a/app.js b/app.js index 40fd9bc167f..6fcc6a880a4 100644 --- a/app.js +++ b/app.js @@ -1,25 +1,26 @@ -const express = require('express') -const logger = require('morgan') -const cors = require('cors') +const express = require("express"); +const logger = require("morgan"); +const cors = require("cors"); -const contactsRouter = require('./routes/api/contacts') +const contactsRouter = require("./routes/api/contacts"); -const app = express() +const app = express(); -const formatsLogger = app.get('env') === 'development' ? 'dev' : 'short' +const formatsLogger = app.get("env") === "development" ? "dev" : "short"; -app.use(logger(formatsLogger)) -app.use(cors()) -app.use(express.json()) +app.use(logger(formatsLogger)); +app.use(cors()); +app.use(express.json()); +// app.use(logger("tiny")); -app.use('/api/contacts', contactsRouter) +app.use("/api/contacts", contactsRouter); app.use((req, res) => { - res.status(404).json({ message: 'Not found' }) -}) + res.status(404).json({ message: "Not found" }); +}); app.use((err, req, res, next) => { - res.status(500).json({ message: err.message }) -}) + res.status(500).json({ message: err.message }); +}); -module.exports = app +module.exports = app; diff --git a/models/contacts.json b/models/contacts.json index a21679132de..d7b0cc64f02 100644 --- a/models/contacts.json +++ b/models/contacts.json @@ -55,8 +55,14 @@ }, { "id": "rsKkOQUi80UsgVPCcLZZW", - "name": "Alec Howard", - "email": "Donec.elementum@scelerisquescelerisquedui.net", - "phone": "(748) 206-2688" + "name": "Lavie", + "email": "tojour@my.com", + "phone": "+74820626881" + }, + { + "id": "1728406702107", + "name": "radu", + "email": "turbomatrixxxl@gmail.com", + "phone": "+40771392871" } -] +] \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index e6d047044e5..773df979b05 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "cors": "2.8.5", "cross-env": "7.0.3", "express": "4.17.1", + "joi": "^17.13.3", "morgan": "1.10.0" }, "devDependencies": { @@ -141,11 +142,48 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "license": "BSD-3-Clause" + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "license": "BSD-3-Clause" + }, "node_modules/@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -155,6 +193,7 @@ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", "dev": true, + "license": "MIT", "dependencies": { "defer-to-connect": "^1.0.1" }, @@ -228,6 +267,7 @@ "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.1.0" } @@ -401,6 +441,7 @@ "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", "integrity": "sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-align": "^3.0.0", "camelcase": "^6.2.0", @@ -423,6 +464,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -465,6 +507,7 @@ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", "dev": true, + "license": "MIT", "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", @@ -483,6 +526,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -498,6 +542,7 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -525,10 +570,11 @@ } }, "node_modules/camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -577,13 +623,15 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cli-boxes": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.1.tgz", "integrity": "sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" }, @@ -592,12 +640,16 @@ } }, "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/color-convert": { @@ -629,6 +681,7 @@ "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dot-prop": "^5.2.0", "graceful-fs": "^4.1.2", @@ -720,6 +773,7 @@ "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -744,8 +798,9 @@ "node_modules/decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "dev": true, + "license": "MIT", "dependencies": { "mimic-response": "^1.0.0" }, @@ -758,6 +813,7 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0.0" } @@ -772,7 +828,8 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/define-properties": { "version": "1.1.3", @@ -816,6 +873,7 @@ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", "dev": true, + "license": "MIT", "dependencies": { "is-obj": "^2.0.0" }, @@ -824,10 +882,11 @@ } }, "node_modules/duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/ee-first": { "version": "1.1.1", @@ -853,6 +912,7 @@ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "dev": true, + "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -925,6 +985,7 @@ "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1573,6 +1634,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0" }, @@ -1629,10 +1691,11 @@ } }, "node_modules/global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, + "license": "MIT", "dependencies": { "ini": "2.0.0" }, @@ -1663,6 +1726,7 @@ "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", "dev": true, + "license": "MIT", "dependencies": { "@sindresorhus/is": "^0.14.0", "@szmarczak/http-timer": "^1.1.2", @@ -1681,10 +1745,11 @@ } }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "dev": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" }, "node_modules/has": { "version": "1.0.3", @@ -1748,15 +1813,17 @@ "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-errors": { "version": "1.7.2", @@ -1818,8 +1885,9 @@ "node_modules/import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -1853,6 +1921,7 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -1936,6 +2005,7 @@ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", "dev": true, + "license": "MIT", "dependencies": { "ci-info": "^2.0.0" }, @@ -2005,6 +2075,7 @@ "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.4.0.tgz", "integrity": "sha512-iwGqO3J21aaSkC7jWnHP/difazwS7SFeIqxv6wEtLU8Y5KlzFTjyqcSIT0d8s4+dDhKytsk9PJZ2BkS5eZwQRQ==", "dev": true, + "license": "MIT", "dependencies": { "global-dirs": "^3.0.0", "is-path-inside": "^3.0.2" @@ -2033,6 +2104,7 @@ "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-5.0.0.tgz", "integrity": "sha512-WW/rQLOazUq+ST/bCAVBp/2oMERWLsR7OrKyt052dNDk4DHcDE0/7QSXITlmi+VBcV13DfIbysG3tZJm5RfdBA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2069,6 +2141,7 @@ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2078,6 +2151,7 @@ "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2140,8 +2214,9 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "dev": true, + "license": "MIT" }, "node_modules/is-weakref": { "version": "1.0.2", @@ -2159,13 +2234,27 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "node_modules/joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "license": "BSD-3-Clause", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2188,8 +2277,9 @@ "node_modules/json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", + "dev": true, + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", @@ -2220,6 +2310,7 @@ "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", "dev": true, + "license": "MIT", "dependencies": { "json-buffer": "3.0.0" } @@ -2229,6 +2320,7 @@ "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", "dev": true, + "license": "MIT", "dependencies": { "package-json": "^6.3.0" }, @@ -2279,27 +2371,17 @@ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.0.0" }, @@ -2311,10 +2393,11 @@ } }, "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2375,15 +2458,17 @@ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2459,6 +2544,7 @@ "integrity": "sha512-gdHMNx47Gw7b3kWxJV64NI+Q5nfl0y5DgDbiVtShiwa7Z0IZ07Ll4RLFo6AjrhzMtoEZn5PDE3/c2AbVsiCkpA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "dependencies": { "chokidar": "^3.5.2", "debug": "^3.2.7", @@ -2487,6 +2573,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, + "license": "MIT", "dependencies": { "ms": "^2.1.1" } @@ -2501,10 +2588,11 @@ } }, "node_modules/nodemon/node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver" } @@ -2550,6 +2638,7 @@ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz", "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2665,6 +2754,7 @@ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -2707,6 +2797,7 @@ "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", "dev": true, + "license": "MIT", "dependencies": { "got": "^9.6.0", "registry-auth-token": "^4.0.0", @@ -2718,10 +2809,11 @@ } }, "node_modules/package-json/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -2819,8 +2911,9 @@ "node_modules/prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -2853,10 +2946,11 @@ "dev": true }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -2876,6 +2970,7 @@ "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.1.1.tgz", "integrity": "sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A==", "dev": true, + "license": "MIT", "dependencies": { "escape-goat": "^2.0.0" }, @@ -2918,6 +3013,7 @@ "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -2932,13 +3028,15 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2968,12 +3066,13 @@ } }, "node_modules/registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", "dev": true, + "license": "MIT", "dependencies": { - "rc": "^1.2.8" + "rc": "1.2.8" }, "engines": { "node": ">=6.0.0" @@ -2984,6 +3083,7 @@ "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", "dev": true, + "license": "MIT", "dependencies": { "rc": "^1.2.8" }, @@ -3025,8 +3125,9 @@ "node_modules/responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dev": true, + "license": "MIT", "dependencies": { "lowercase-keys": "^1.0.0" } @@ -3057,13 +3158,11 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3076,6 +3175,7 @@ "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^6.3.0" }, @@ -3084,10 +3184,11 @@ } }, "node_modules/semver-diff/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -3186,10 +3287,11 @@ } }, "node_modules/signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", - "dev": true + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" }, "node_modules/slice-ansi": { "version": "4.0.0", @@ -3356,6 +3458,7 @@ "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3442,6 +3545,7 @@ "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, + "license": "MIT", "dependencies": { "is-typedarray": "^1.0.0" } @@ -3472,6 +3576,7 @@ "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", "dev": true, + "license": "MIT", "dependencies": { "crypto-random-string": "^2.0.0" }, @@ -3492,6 +3597,7 @@ "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-5.1.0.tgz", "integrity": "sha512-ItnICHbeMh9GqUy31hFPrD1kcuZ3rpxDZbf4KUDavXwS0bW5m7SLbDQpGX3UYr072cbrF5hFUs3r5tUsPwjfHw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boxen": "^5.0.0", "chalk": "^4.1.0", @@ -3527,8 +3633,9 @@ "node_modules/url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "dev": true, + "license": "MIT", "dependencies": { "prepend-http": "^2.0.0" }, @@ -3593,6 +3700,7 @@ "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", "dev": true, + "license": "MIT", "dependencies": { "string-width": "^4.0.0" }, @@ -3614,6 +3722,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -3637,6 +3746,7 @@ "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "is-typedarray": "^1.0.0", @@ -3649,15 +3759,10 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } }, "dependencies": { @@ -3757,6 +3862,37 @@ "strip-json-comments": "^3.1.1" } }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" + }, "@sindresorhus/is": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", @@ -4051,9 +4187,9 @@ "dev": true }, "camelcase": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.1.tgz", - "integrity": "sha512-tVI4q5jjFV5CavAU8DXfza/TJcZutVKo/5Foskmsqcm0MsL91moHvwiGNnqaa2o6PF/7yT5ikDRcVcl8Rj6LCA==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true }, "chalk": { @@ -4095,9 +4231,9 @@ "dev": true }, "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -4206,7 +4342,7 @@ "decompress-response": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "integrity": "sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA==", "dev": true, "requires": { "mimic-response": "^1.0.0" @@ -4268,9 +4404,9 @@ } }, "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", + "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", "dev": true }, "ee-first": { @@ -4897,9 +5033,9 @@ } }, "global-dirs": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.0.tgz", - "integrity": "sha512-v8ho2DS5RiCjftj1nD9NmnfaOzTdud7RRnVd9kFNOjqZbISlx5DQ+OrTkywgd0dIt7oFCvKetZSHoHcP3sDdiA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-3.0.1.tgz", + "integrity": "sha512-NBcGGFbBA9s1VzD41QXDG+3++t9Mn5t1FpLdhESY6oKY4gYTFpX4wO3sqGUa0Srjtbfj3szX0RnemmrVRUdULA==", "dev": true, "requires": { "ini": "2.0.0" @@ -4934,9 +5070,9 @@ } }, "graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, "has": { @@ -4982,9 +5118,9 @@ "dev": true }, "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", "dev": true }, "http-errors": { @@ -5032,7 +5168,7 @@ "import-lazy": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "integrity": "sha512-m7ZEHgtw69qOGw+jwxXkHlrlIPdTGkyh66zXZ1ajZbxkDBNjSY/LGbmjc7h0s2ELsUDTAhFr55TrPSSqJGPG0A==", "dev": true }, "imurmurhash": { @@ -5246,7 +5382,7 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", "dev": true }, "is-weakref": { @@ -5269,6 +5405,18 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" }, + "joi": { + "version": "17.13.3", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", + "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "requires": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5288,7 +5436,7 @@ "json-buffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", + "integrity": "sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ==", "dev": true }, "json-schema-traverse": { @@ -5368,15 +5516,6 @@ "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", "dev": true }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, "make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", @@ -5387,9 +5526,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -5434,9 +5573,9 @@ "dev": true }, "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -5531,9 +5670,9 @@ "dev": true }, "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true }, "supports-color": { @@ -5687,9 +5826,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -5760,7 +5899,7 @@ "prepend-http": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", + "integrity": "sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA==", "dev": true }, "progress": { @@ -5785,9 +5924,9 @@ "dev": true }, "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", + "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", "dev": true, "requires": { "end-of-stream": "^1.1.0", @@ -5851,7 +5990,7 @@ "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "dev": true } } @@ -5872,12 +6011,12 @@ "dev": true }, "registry-auth-token": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.1.tgz", - "integrity": "sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw==", + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.2.2.tgz", + "integrity": "sha512-PC5ZysNb42zpFME6D/XlIgtNGdTl8bBOCw90xQLVMpzuuubJKYDWFAEuUNc+Cn8Z8724tg2SDhDRrkVEsqfDMg==", "dev": true, "requires": { - "rc": "^1.2.8" + "rc": "1.2.8" } }, "registry-url": { @@ -5914,7 +6053,7 @@ "responselike": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "integrity": "sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ==", "dev": true, "requires": { "lowercase-keys": "^1.0.0" @@ -5940,13 +6079,10 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "dev": true }, "semver-diff": { "version": "3.1.1", @@ -5958,9 +6094,9 @@ }, "dependencies": { "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true } } @@ -6048,9 +6184,9 @@ } }, "signal-exit": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.6.tgz", - "integrity": "sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ==", + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "slice-ansi": { @@ -6315,7 +6451,7 @@ "url-parse-lax": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "integrity": "sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ==", "dev": true, "requires": { "prepend-http": "^2.0.0" @@ -6407,12 +6543,6 @@ "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true } } } diff --git a/package.json b/package.json index 5045e827160..3c7b3ab3e2a 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "cors": "2.8.5", "cross-env": "7.0.3", "express": "4.17.1", + "joi": "^17.13.3", "morgan": "1.10.0" }, "devDependencies": { diff --git a/routes/api/contacts.js b/routes/api/contacts.js index 46a22a4d464..595b708ff21 100644 --- a/routes/api/contacts.js +++ b/routes/api/contacts.js @@ -2,24 +2,217 @@ const express = require("express"); const router = express.Router(); +const Joi = require("joi"); + +const { + listContacts, + listContactById, + addContact, + deleteContact, + patchContact, +} = require("../../services/actions"); + router.get("/", async (req, res, next) => { - res.json({ message: "Homework in progress !!!" }); + try { + const contacts = await listContacts(); + res.status(200).json({ + status: "success", + code: "200", + message: "Contacts listed with success !!!", + data: contacts, + }); + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: "Contacts listing error...!", + }); + } }); router.get("/:contactId", async (req, res, next) => { - res.json({ message: "template message" }); + const id = req.params.contactId; + console.log(id); + + try { + const contact = await listContactById(id); + // console.table(contact); + + if (!contact) { + return res.status(404).json({ + status: "error", + code: 404, + message: `Contact with id: ${id} does not exists...!`, + }); + } + + res.status(201).json({ + status: "success", + code: "201", + message: `Contact with id: ${id} found !!!`, + data: contact, + }); + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: `Contact searching error...!`, + }); + } +}); + +const nameSchema = Joi.object({ + // eslint-disable-next-line prefer-regex-literals + name: Joi.string().pattern(new RegExp("^[a-zA-Z]{3,30}$")), +}); + +const emailSchema = Joi.object({ + email: Joi.string().email({ + minDomainSegments: 2, + tlds: { allow: ["com", "net"] }, + }), +}); + +const phoneSchema = Joi.object({ + phone: Joi.string().pattern(/^\+?\d{9,30}$/), }); router.post("/", async (req, res, next) => { - res.json({ message: "template message" }); + const { name, email, phone } = req.body; + + if (!name || name === null) { + res.status(400).json({ message: "missing required name field" }); + } + if (!email || email === null) { + res.status(400).json({ message: "missing required email field" }); + } + + if (!phone || phone === null) { + res.status(400).json({ message: "missing required phone number field" }); + } + + const { error } = nameSchema.validate({ name: name }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = emailSchema.validate({ email: email }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = phoneSchema.validate({ phone: phone }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + try { + const newContacts = await addContact({ + name: name, + email: email, + phone: phone, + }); + + if (newContacts === "nameIs") { + res.status(409).json({ + message: + "The contact with this name already exists...! Please provide different info ! ", + }); + } else if (newContacts === "emailIs") { + res.status(409).json({ + message: + "This email already exists...! Please provide different info ! ", + }); + } else if (newContacts === "phoneIs") { + res.status(409).json({ + message: + "This phone number already exists...! Please provide different info ! ", + }); + } else { + res.status(201).json({ + status: "success", + code: 201, + message: `Success adding contact: name:${name}, email:${email}, phone:${phone} !`, + data: newContacts, + }); + } + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: `Error adding contact...!`, + }); + } + } + } + } }); router.delete("/:contactId", async (req, res, next) => { - res.json({ message: "template message" }); + const id = req.params.contactId; + + try { + const deletedContact = await deleteContact(id); + if (deletedContact === "isNot") { + res.status(404).json({ message: `contact with id:${id} not found` }); + } else { + res.status(200).json({ message: `contact with id:${id} deleted` }); + } + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: "Error deleting contact...!", + }); + } }); router.put("/:contactId", async (req, res, next) => { - res.json({ message: "template message" }); + const id = req.params.contactId; + const { name, email, phone } = req.body; + + if (!name && !email && !phone) { + res.status(400).json({ message: "missing fields" }); + return; + } + + try { + const updatedContacts = await patchContact({ + id: id, + name: name, + email: email, + phone: phone, + }); + + if (updatedContacts === false) { + res.status(404).json({ message: "Contact for update is not found...!" }); + } else { + const { error } = nameSchema.validate({ name: name }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = emailSchema.validate({ email: email }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = phoneSchema.validate({ phone: phone }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + res.status(200).json({ + status: "success", + code: 200, + message: `Success updating contact with id:${id}...!`, + data: updatedContacts, + }); + } + } + } + } + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: "Error updating contact...!", + }); + } }); module.exports = router; diff --git a/server.js b/server.js index db330824656..d3923aa3393 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,5 @@ -const app = require('./app') +const app = require("./app"); app.listen(3000, () => { - console.log("Server running. Use our API on port: 3000") -}) + console.log("Server running. Use our API on port: 3000"); +}); diff --git a/services/actions.js b/services/actions.js new file mode 100644 index 00000000000..ee0e0d8fbc7 --- /dev/null +++ b/services/actions.js @@ -0,0 +1,232 @@ +const path = require("path"); +const fs = require("fs").promises; // Use the promise-based version of fs + +const contactsPath = path.join(__dirname, "..", "models", "contacts.json"); +// console.log(tasksPath); + +//! list contacts +async function listContacts() { + try { + const data = await fs.readFile(contactsPath, "utf-8"); + const contacts = JSON.parse(data); + + console.table(contacts); + return contacts; + } catch (error) { + console.log("Error on reading file...!"); + throw error; + } +} + +// listContacts(); + +//! list contact by id +async function listContactById(contactId) { + try { + const data = await fs.readFile(contactsPath, "utf-8"); + const contacts = JSON.parse(data); + + const contact = contacts.find((contact) => contact.id === contactId); + + if (!contact) { + console.log(`Contact with id ${contactId} does not exists !`); + return false; + } + + // console.table(contact); + return contact; + } catch (error) { + console.log("Error on reading file...!"); + throw error; + } +} + +// listContactById({ id: "AeHIrLTr6JkxGE6SN-0Rw" }); + +//! add contact +async function addContact({ name, email, phone }) { + if (!name) { + console.log("Please enter name ...!"); + + return; + } + if (!email) { + console.log("Please enter email ...!"); + + return; + } + + if (!phone) { + console.log("Please enter phone ...!"); + + return; + } + + try { + const data = await fs.readFile(contactsPath, "utf-8"); + const contacts = JSON.parse(data); + + const isName = contacts.some((contact) => contact.name === name); + + if (isName) { + return "nameIs"; + } + + const isEmail = contacts.some((contact) => contact.email === email); + + if (isEmail) { + return "emailIs"; + } + + const isPhone = contacts.some((contact) => contact.phone === phone); + + if (isPhone) { + return "phoneIs"; + } + + const newContact = { + id: String(Date.now()), + name: name, + email: email, + phone: phone, + }; + + contacts.push(newContact); + + await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2)); + + // console.table(contacts); + return contacts; + } catch (error) { + console.log("Error on reading file...!", error); + throw error; + } +} + +//! delete contact +async function deleteContact(id) { + try { + const data = await fs.readFile(contactsPath, "utf-8"); + const contacts = JSON.parse(data); + + const isId = contacts.some((task) => id === task.id); + if (!isId) { + console.log("This user does not exists...!"); + + return "isNot"; + } + + const newContacts = contacts.filter((contact) => contact.id !== id); + + await fs.writeFile(contactsPath, JSON.stringify(newContacts, null, 2)); + + // console.table(newContacts); + return newContacts; + } catch (error) { + console.log("Error on reading file...!", error); + throw error; + } +} + +// deleteTask("sa uit"); + +//! update tasks +// async function updateTask({ id, task, completed }) { +// try { +// const data = await fs.readFile(contactsPath, "utf-8"); +// const tasks = JSON.parse(data); + +// // Find the task by id +// const taskToUpdate = tasks.find((taskItem) => taskItem.id === id); + +// if (taskToUpdate === undefined) { +// console.log("Acest task nu exista...! Vom crea unul nou !"); + +// // Create a new task if it doesn't exist +// const newTask = { +// id: id, +// task: typeof task === "undefined" ? "trebuie sa adaugi un task" : task, +// completed: typeof completed === "undefined" ? false : completed, +// }; + +// tasks.push(newTask); + +// // Write updated tasks to file and return +// await fs.writeFile(contactsPath, JSON.stringify(tasks, null, 2)); +// return tasks; +// } + +// // Update the task with new values, keeping old ones if not provided +// const updatedTask = { +// id: taskToUpdate.id, +// task: typeof task === "undefined" ? taskToUpdate.task : task, +// completed: +// typeof completed === "undefined" ? taskToUpdate.completed : completed, +// }; + +// // Filter out the old task and create a new list +// const restTasks = tasks.filter((taskItem) => taskItem.id !== id); +// const newTasks = [...restTasks, updatedTask]; +// // Write the updated task list back to the file +// await fs.writeFile(contactsPath, JSON.stringify(newTasks, null, 2)); + +// // Return the updated task list +// return newTasks; +// } catch (error) { +// console.log("Eroare la citire fisier...!", error); +// throw error; +// } +// } + +// Example usage: +// updateTask({ id: "2", task: "sa citesc o carte", completed: true }); + +// Call the function with an object +// updateTask({ id: "2", task: "sa uitt", completed: false }); +// updateTask({ id: "2", completed: true }); + +//! Inlocuieste resursa partial cu findIndex +async function patchContact({ id, name, email, phone }) { + try { + const data = await fs.readFile(contactsPath, "utf-8"); + const contacts = JSON.parse(data); + // console.log(tasks); + + const contactIndex = contacts.findIndex((contact) => contact.id === id); + + if (contactIndex === -1) { + console.log("This contact does not exists...!"); + return false; + } + + if (name !== undefined) { + contacts[contactIndex].name = name; + } + + if (email !== undefined) { + contacts[contactIndex].email = email; + } + + if (phone !== undefined) { + contacts[contactIndex].phone = phone; + } + + await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2)); + + // console.table(tasks); + return contacts; + } catch (error) { + console.log("Error on patching file...!", error); + throw error; + } +} + +// patchTask({ id: "2", task: "sa ma uitt", completed: false }); + +module.exports = { + listContacts, + listContactById, + addContact, + deleteContact, + patchContact, +}; From 194f74ce5370d7c5843aa4c26652fd66a7e44b09 Mon Sep 17 00:00:00 2001 From: Radu Bogdan Naramzoiu Date: Tue, 8 Oct 2024 23:56:15 +0300 Subject: [PATCH 3/5] Last checkout and commented all unnecesary console.logs ! --- app.js | 4 ++- cors.js | 5 +++ routes/api/contacts.js | 2 +- services/actions.js | 74 ++++-------------------------------------- 4 files changed, 16 insertions(+), 69 deletions(-) create mode 100644 cors.js diff --git a/app.js b/app.js index 6fcc6a880a4..8bfc22435e7 100644 --- a/app.js +++ b/app.js @@ -1,6 +1,8 @@ const express = require("express"); const logger = require("morgan"); + const cors = require("cors"); +const corsOptions = require("./cors"); const contactsRouter = require("./routes/api/contacts"); @@ -9,7 +11,7 @@ const app = express(); const formatsLogger = app.get("env") === "development" ? "dev" : "short"; app.use(logger(formatsLogger)); -app.use(cors()); +app.use(cors(corsOptions)); app.use(express.json()); // app.use(logger("tiny")); diff --git a/cors.js b/cors.js new file mode 100644 index 00000000000..12da883a5f9 --- /dev/null +++ b/cors.js @@ -0,0 +1,5 @@ +const corsOptions = { + origin: "*", +}; + +module.exports = { corsOptions }; diff --git a/routes/api/contacts.js b/routes/api/contacts.js index 595b708ff21..98c168a310c 100644 --- a/routes/api/contacts.js +++ b/routes/api/contacts.js @@ -32,7 +32,7 @@ router.get("/", async (req, res, next) => { router.get("/:contactId", async (req, res, next) => { const id = req.params.contactId; - console.log(id); + // console.log(id); try { const contact = await listContactById(id); diff --git a/services/actions.js b/services/actions.js index ee0e0d8fbc7..c91a9d3d7b0 100644 --- a/services/actions.js +++ b/services/actions.js @@ -10,7 +10,7 @@ async function listContacts() { const data = await fs.readFile(contactsPath, "utf-8"); const contacts = JSON.parse(data); - console.table(contacts); + // console.table(contacts); return contacts; } catch (error) { console.log("Error on reading file...!"); @@ -29,7 +29,7 @@ async function listContactById(contactId) { const contact = contacts.find((contact) => contact.id === contactId); if (!contact) { - console.log(`Contact with id ${contactId} does not exists !`); + // console.log(`Contact with id ${contactId} does not exists !`); return false; } @@ -46,18 +46,18 @@ async function listContactById(contactId) { //! add contact async function addContact({ name, email, phone }) { if (!name) { - console.log("Please enter name ...!"); + // console.log("Please enter name ...!"); return; } if (!email) { - console.log("Please enter email ...!"); + // console.log("Please enter email ...!"); return; } if (!phone) { - console.log("Please enter phone ...!"); + // console.log("Please enter phone ...!"); return; } @@ -128,74 +128,17 @@ async function deleteContact(id) { } } -// deleteTask("sa uit"); - -//! update tasks -// async function updateTask({ id, task, completed }) { -// try { -// const data = await fs.readFile(contactsPath, "utf-8"); -// const tasks = JSON.parse(data); - -// // Find the task by id -// const taskToUpdate = tasks.find((taskItem) => taskItem.id === id); - -// if (taskToUpdate === undefined) { -// console.log("Acest task nu exista...! Vom crea unul nou !"); - -// // Create a new task if it doesn't exist -// const newTask = { -// id: id, -// task: typeof task === "undefined" ? "trebuie sa adaugi un task" : task, -// completed: typeof completed === "undefined" ? false : completed, -// }; - -// tasks.push(newTask); - -// // Write updated tasks to file and return -// await fs.writeFile(contactsPath, JSON.stringify(tasks, null, 2)); -// return tasks; -// } - -// // Update the task with new values, keeping old ones if not provided -// const updatedTask = { -// id: taskToUpdate.id, -// task: typeof task === "undefined" ? taskToUpdate.task : task, -// completed: -// typeof completed === "undefined" ? taskToUpdate.completed : completed, -// }; - -// // Filter out the old task and create a new list -// const restTasks = tasks.filter((taskItem) => taskItem.id !== id); -// const newTasks = [...restTasks, updatedTask]; -// // Write the updated task list back to the file -// await fs.writeFile(contactsPath, JSON.stringify(newTasks, null, 2)); - -// // Return the updated task list -// return newTasks; -// } catch (error) { -// console.log("Eroare la citire fisier...!", error); -// throw error; -// } -// } - -// Example usage: -// updateTask({ id: "2", task: "sa citesc o carte", completed: true }); - -// Call the function with an object -// updateTask({ id: "2", task: "sa uitt", completed: false }); -// updateTask({ id: "2", completed: true }); - //! Inlocuieste resursa partial cu findIndex async function patchContact({ id, name, email, phone }) { try { const data = await fs.readFile(contactsPath, "utf-8"); const contacts = JSON.parse(data); - // console.log(tasks); + // console.log(contacts); const contactIndex = contacts.findIndex((contact) => contact.id === id); if (contactIndex === -1) { - console.log("This contact does not exists...!"); + // console.log("This contact does not exists...!"); return false; } @@ -213,7 +156,6 @@ async function patchContact({ id, name, email, phone }) { await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2)); - // console.table(tasks); return contacts; } catch (error) { console.log("Error on patching file...!", error); @@ -221,8 +163,6 @@ async function patchContact({ id, name, email, phone }) { } } -// patchTask({ id: "2", task: "sa ma uitt", completed: false }); - module.exports = { listContacts, listContactById, From baaac797ccfd51d5b5a44fa092309872b800549f Mon Sep 17 00:00:00 2001 From: Radu Bogdan Naramzoiu Date: Wed, 16 Oct 2024 14:25:50 +0300 Subject: [PATCH 4/5] finished homework with all checkins --- controllers/index.js | 236 ++++++++++++++++++++ cors.js | 9 +- models/contacts.js | 19 -- models/contacts.json | 68 ------ models/index.js | 29 +++ models/schemas/contactsShema.js | 29 +++ package-lock.json | 368 ++++++++++++++++++++++++++++++-- package.json | 2 + routes/api/contacts.js | 213 +----------------- server.js | 22 +- services/actions.js | 172 --------------- services/index.js | 29 +++ 12 files changed, 713 insertions(+), 483 deletions(-) create mode 100644 controllers/index.js delete mode 100644 models/contacts.js delete mode 100644 models/contacts.json create mode 100644 models/index.js create mode 100644 models/schemas/contactsShema.js delete mode 100644 services/actions.js create mode 100644 services/index.js diff --git a/controllers/index.js b/controllers/index.js new file mode 100644 index 00000000000..42308a214f3 --- /dev/null +++ b/controllers/index.js @@ -0,0 +1,236 @@ +const models = require("../models/index"); + +const Joi = require("joi"); + +const getContacts = async (req, res, next) => { + try { + const contacts = await models.listContacts(); + res.status(200).json({ + status: "success", + code: "200", + message: "Contacts listed with success !!!", + data: contacts, + }); + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: "Contacts listing error...!", + }); + next(error); + } +}; + +const getContactsById = async (req, res, next) => { + const id = req.params.contactId; + // console.log(id); + + try { + const contact = await models.getContactById(id); + // console.table(contact); + + if (!contact) { + return res.status(404).json({ + status: "error", + code: 404, + message: `Contact with id: ${id} does not exists...!`, + }); + } + + res.status(201).json({ + status: "success", + code: "201", + message: `Contact with id: ${id} found !!!`, + data: contact, + }); + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: `Contact searching error...!`, + }); + next(error); + } +}; + +const nameSchema = Joi.object({ + // eslint-disable-next-line prefer-regex-literals + name: Joi.string().pattern(new RegExp("^[a-zA-Z]{4,}(\\s+[a-zA-Z]+)*$")), +}); + +const emailSchema = Joi.object({ + email: Joi.string().email({ + minDomainSegments: 2, + tlds: { allow: ["com", "net", "uk"] }, + }), +}); + +const phoneSchema = Joi.object({ + phone: Joi.string().pattern(/^[+]?[\d\s,./\\\-()]{9,}$/), +}); + +const createContact = async (req, res, next) => { + const { name, email, phone, favorite } = req.body; + + const { error } = nameSchema.validate({ name: name }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = emailSchema.validate({ email: email }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + const { error } = phoneSchema.validate({ phone: phone }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } else { + try { + const newContact = await models.addContact({ + name: name, + email: email, + phone: phone, + favorite: favorite, + }); + + res.status(201).json({ + status: "success", + code: 201, + message: `Success adding contact: name:${name}, email:${email}, phone:${phone} !`, + data: newContact, + }); + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: error.message, + }); + next(error); + } + } + } + } +}; + +const deleteContact = async (req, res, next) => { + const id = req.params.contactId; + + try { + const deletedContact = await models.removeContact(id); + if (!deletedContact) { + res.status(404).json({ message: `contact with id:${id} not found` }); + } else { + res.status(200).json({ message: `contact with id:${id} deleted` }); + } + } catch (error) { + res.status(500).json({ + status: "fail", + code: 500, + message: "Error deleting contact...!", + }); + next(error); + } +}; + +const updateContact = async (req, res, next) => { + const id = req.params.contactId; + const { name, email, phone } = req.body; + + // Check for at least one field to update + if (!name && !email && !phone) { + return res.status(400).json({ message: "missing fields" }); + } + + const updatedElements = {}; + + // Validate name if provided + if (name || name !== undefined) { + const { error } = nameSchema.validate({ name: name }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + updatedElements.name = name; + } + + // Validate email if provided + if (email || email !== undefined) { + const { error } = emailSchema.validate({ email: email }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + updatedElements.email = email; + } + + // Validate phone if provided + if (phone || phone !== undefined) { + const { error } = phoneSchema.validate({ phone: phone }); + if (error) { + return res.status(400).json({ error: error.details[0].message }); + } + updatedElements.phone = phone; + } + + try { + const updatedContact = await models.updateContact(id, { + ...updatedElements, + }); + + if (!updatedContact) { + return res + .status(404) + .json({ message: "Contact for update is not found...!" }); + } + + res.status(200).json({ + status: "success", + code: 200, + message: `Success updating contact with id:${id}...!`, + data: updatedContact, + }); + } catch (error) { + console.error("Error updating contact:", error); + res.status(500).json({ + status: "fail", + code: 500, + message: "Error updating contact...!", + }); + next(error); + } +}; + +const updateFavoriteStatus = async (req, res, next) => { + const id = req.params.contactId; + const { favorite = false } = req.body; + + try { + // console.log(id); + + const result = await models.updateContact(id, { favorite }); + + if (result) { + res.json({ + status: "success", + code: 200, + data: result, + }); + } else { + res.status(404).json({ + status: "error", + code: 404, + message: `Not found Contact id: ${id}`, + data: "Not Found", + }); + } + } catch (error) { + console.error(error); + next(error); + } +}; + +module.exports = { + getContacts, + getContactsById, + createContact, + deleteContact, + updateContact, + updateFavoriteStatus, +}; diff --git a/cors.js b/cors.js index 12da883a5f9..603244d631f 100644 --- a/cors.js +++ b/cors.js @@ -1,5 +1,12 @@ const corsOptions = { + // Permite doar cereri de la această origine origin: "*", + // Permite doar metodele GET și POST + methods: "GET,POST", + // Returnează un status 204 pentru cererile prefligth (OPTIONS) + optionsSuccessStatus: 204, }; -module.exports = { corsOptions }; +module.exports = { + corsOptions, +}; diff --git a/models/contacts.js b/models/contacts.js deleted file mode 100644 index 409d11c7c09..00000000000 --- a/models/contacts.js +++ /dev/null @@ -1,19 +0,0 @@ -// const fs = require('fs/promises') - -const listContacts = async () => {} - -const getContactById = async (contactId) => {} - -const removeContact = async (contactId) => {} - -const addContact = async (body) => {} - -const updateContact = async (contactId, body) => {} - -module.exports = { - listContacts, - getContactById, - removeContact, - addContact, - updateContact, -} diff --git a/models/contacts.json b/models/contacts.json deleted file mode 100644 index d7b0cc64f02..00000000000 --- a/models/contacts.json +++ /dev/null @@ -1,68 +0,0 @@ -[ - { - "id": "AeHIrLTr6JkxGE6SN-0Rw", - "name": "Allen Raymond", - "email": "nulla.ante@vestibul.co.uk", - "phone": "(992) 914-3792" - }, - { - "id": "qdggE76Jtbfd9eWJHrssH", - "name": "Chaim Lewis", - "email": "dui.in@egetlacus.ca", - "phone": "(294) 840-6685" - }, - { - "id": "drsAJ4SHPYqZeG-83QTVW", - "name": "Kennedy Lane", - "email": "mattis.Cras@nonenimMauris.net", - "phone": "(542) 451-7038" - }, - { - "id": "vza2RIzNGIwutCVCs4mCL", - "name": "Wylie Pope", - "email": "est@utquamvel.net", - "phone": "(692) 802-2949" - }, - { - "id": "05olLMgyVQdWRwgKfg5J6", - "name": "Cyrus Jackson", - "email": "nibh@semsempererat.com", - "phone": "(501) 472-5218" - }, - { - "id": "1DEXoP8AuCGYc1YgoQ6hw", - "name": "Abbot Franks", - "email": "scelerisque@magnis.org", - "phone": "(186) 568-3720" - }, - { - "id": "Z5sbDlS7pCzNsnAHLtDJd", - "name": "Reuben Henry", - "email": "pharetra.ut@dictum.co.uk", - "phone": "(715) 598-5792" - }, - { - "id": "C9sjBfCo4UJCWjzBnOtxl", - "name": "Simon Morton", - "email": "dui.Fusce.diam@Donec.com", - "phone": "(233) 738-2360" - }, - { - "id": "e6ywwRe4jcqxXfCZOj_1e", - "name": "Thomas Lucas", - "email": "nec@Nulla.com", - "phone": "(704) 398-7993" - }, - { - "id": "rsKkOQUi80UsgVPCcLZZW", - "name": "Lavie", - "email": "tojour@my.com", - "phone": "+74820626881" - }, - { - "id": "1728406702107", - "name": "radu", - "email": "turbomatrixxxl@gmail.com", - "phone": "+40771392871" - } -] \ No newline at end of file diff --git a/models/index.js b/models/index.js new file mode 100644 index 00000000000..76b56c316a1 --- /dev/null +++ b/models/index.js @@ -0,0 +1,29 @@ +const Contact = require("./schemas/contactsShema"); + +const listContacts = async () => { + return Contact.find(); +}; + +const getContactById = async (contactId) => { + return Contact.findOne({ _id: contactId }); +}; + +const removeContact = async (contactId) => { + return Contact.findByIdAndDelete(contactId); +}; + +const addContact = async ({ name, email, phone, favorite }) => { + return Contact.create({ name, email, phone, favorite }); +}; + +const updateContact = async (contactId, fields) => { + return Contact.findByIdAndUpdate({ _id: contactId }, fields, { new: true }); +}; + +module.exports = { + listContacts, + getContactById, + removeContact, + addContact, + updateContact, +}; diff --git a/models/schemas/contactsShema.js b/models/schemas/contactsShema.js new file mode 100644 index 00000000000..471b25ccf49 --- /dev/null +++ b/models/schemas/contactsShema.js @@ -0,0 +1,29 @@ +const mongoose = require("mongoose"); +const Schema = mongoose.Schema; + +const contact = new Schema( + { + name: { + type: String, + required: [true, "Set name for contact"], + unique: true, + }, + email: { + type: String, + unique: true, + }, + phone: { + type: String, + unique: true, + }, + favorite: { + type: Boolean, + default: false, + }, + }, + { versionKey: false, timestamps: true } +); + +const Contact = mongoose.model("contacts", contact); + +module.exports = Contact; diff --git a/package-lock.json b/package-lock.json index 773df979b05..540406a6a63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,8 +10,10 @@ "dependencies": { "cors": "2.8.5", "cross-env": "7.0.3", + "dotenv": "^16.4.5", "express": "4.17.1", "joi": "^17.13.3", + "mongoose": "^8.7.1", "morgan": "1.10.0" }, "devDependencies": { @@ -157,6 +159,15 @@ "@hapi/hoek": "^9.0.0" } }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -207,6 +218,21 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -494,6 +520,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -782,7 +817,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -881,6 +915,18 @@ "node": ">=8" } }, + "node_modules/dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -2305,6 +2351,15 @@ "json5": "lib/cli.js" } }, + "node_modules/kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -2410,6 +2465,12 @@ "node": ">= 0.6" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -2482,6 +2543,90 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "node_modules/mongodb": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", + "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/mongoose": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.1.tgz", + "integrity": "sha512-RpNMyhyzLVCVbf8xTVbrf/18G3MqQzNw5pJdvOJ60fzbCa3cOZzz9L+8XpqzBXtRlgZGWv0T7MmOtvrT8ocp1Q==", + "license": "MIT", + "dependencies": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.9.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "engines": { + "node": ">=16.20.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mongoose/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, "node_modules/morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -2518,11 +2663,31 @@ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "license": "MIT", + "dependencies": { + "debug": "4.x" + }, + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/natural-compare": { "version": "1.4.0", @@ -2957,10 +3122,10 @@ } }, "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3286,6 +3451,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==", + "license": "MIT" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -3310,6 +3481,15 @@ "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -3495,6 +3675,18 @@ "nodetouch": "bin/nodetouch.js" } }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/tsconfig-paths": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", @@ -3665,6 +3857,28 @@ "node": ">= 0.8" } }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "license": "MIT", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -3875,6 +4089,14 @@ "@hapi/hoek": "^9.0.0" } }, + "@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "requires": { + "sparse-bitfield": "^3.0.3" + } + }, "@sideway/address": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", @@ -3914,6 +4136,19 @@ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "dev": true }, + "@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "requires": { + "@types/webidl-conversions": "*" + } + }, "abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", @@ -4133,6 +4368,11 @@ "fill-range": "^7.0.1" } }, + "bson": { + "version": "6.8.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", + "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -4334,7 +4574,6 @@ "version": "4.3.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -4403,6 +4642,11 @@ "is-obj": "^2.0.0" } }, + "dotenv": { + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==" + }, "duplexer3": { "version": "0.1.5", "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz", @@ -5460,6 +5704,11 @@ "minimist": "^1.2.0" } }, + "kareem": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", + "integrity": "sha512-C3iHfuGUXK2u8/ipq9LfjFfXFxAZMQJJq7vLS45r3D9Y2xQ/m4S8zaR4zMLFWh9AsNPXmcFfUDhTEO8UIC/V6Q==" + }, "keyv": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", @@ -5538,6 +5787,11 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" }, + "memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, "merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -5587,6 +5841,46 @@ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, + "mongodb": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.9.0.tgz", + "integrity": "sha512-UMopBVx1LmEUbW/QE0Hw18u583PEDVQmUmVzzBRH0o/xtE9DBRA5ZYLOjpLIa03i8FXjzvQECJcqoMvCXftTUA==", + "requires": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + } + }, + "mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "requires": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "mongoose": { + "version": "8.7.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-8.7.1.tgz", + "integrity": "sha512-RpNMyhyzLVCVbf8xTVbrf/18G3MqQzNw5pJdvOJ60fzbCa3cOZzz9L+8XpqzBXtRlgZGWv0T7MmOtvrT8ocp1Q==", + "requires": { + "bson": "^6.7.0", + "kareem": "2.6.3", + "mongodb": "6.9.0", + "mpath": "0.9.0", + "mquery": "5.0.0", + "ms": "2.1.3", + "sift": "17.1.3" + }, + "dependencies": { + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + } + } + }, "morgan": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", @@ -5619,11 +5913,23 @@ } } }, + "mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==" + }, + "mquery": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-5.0.0.tgz", + "integrity": "sha512-iQMncpmEK8R8ncT8HJGsGc9Dsp8xcgYMVSbs5jgnm1lFHTZqMJTUWTDx1LBO8+mK3tPNZWFLBghQEIOULSTHZg==", + "requires": { + "debug": "4.x" + } + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "natural-compare": { "version": "1.4.0", @@ -5934,10 +6240,9 @@ } }, "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" }, "pupa": { "version": "2.1.1", @@ -6183,6 +6488,11 @@ "object-inspect": "^1.9.0" } }, + "sift": { + "version": "17.1.3", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.3.tgz", + "integrity": "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ==" + }, "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -6200,6 +6510,14 @@ "is-fullwidth-code-point": "^3.0.0" } }, + "sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "requires": { + "memory-pager": "^1.0.2" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -6340,6 +6658,14 @@ "nopt": "~1.0.10" } }, + "tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "requires": { + "punycode": "^2.3.0" + } + }, "tsconfig-paths": { "version": "3.12.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz", @@ -6473,6 +6799,20 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==" + }, + "whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "requires": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + } + }, "which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 3c7b3ab3e2a..6a18040681e 100644 --- a/package.json +++ b/package.json @@ -11,8 +11,10 @@ "dependencies": { "cors": "2.8.5", "cross-env": "7.0.3", + "dotenv": "^16.4.5", "express": "4.17.1", "joi": "^17.13.3", + "mongoose": "^8.7.1", "morgan": "1.10.0" }, "devDependencies": { diff --git a/routes/api/contacts.js b/routes/api/contacts.js index 98c168a310c..897dd9eb063 100644 --- a/routes/api/contacts.js +++ b/routes/api/contacts.js @@ -2,217 +2,18 @@ const express = require("express"); const router = express.Router(); -const Joi = require("joi"); +const controller = require("../../controllers/index"); -const { - listContacts, - listContactById, - addContact, - deleteContact, - patchContact, -} = require("../../services/actions"); +router.get("/", controller.getContacts); -router.get("/", async (req, res, next) => { - try { - const contacts = await listContacts(); - res.status(200).json({ - status: "success", - code: "200", - message: "Contacts listed with success !!!", - data: contacts, - }); - } catch (error) { - res.status(500).json({ - status: "fail", - code: 500, - message: "Contacts listing error...!", - }); - } -}); +router.get("/:contactId", controller.getContactsById); -router.get("/:contactId", async (req, res, next) => { - const id = req.params.contactId; - // console.log(id); +router.post("/", controller.createContact); - try { - const contact = await listContactById(id); - // console.table(contact); +router.delete("/:contactId", controller.deleteContact); - if (!contact) { - return res.status(404).json({ - status: "error", - code: 404, - message: `Contact with id: ${id} does not exists...!`, - }); - } +router.put("/:contactId", controller.updateContact); - res.status(201).json({ - status: "success", - code: "201", - message: `Contact with id: ${id} found !!!`, - data: contact, - }); - } catch (error) { - res.status(500).json({ - status: "fail", - code: 500, - message: `Contact searching error...!`, - }); - } -}); - -const nameSchema = Joi.object({ - // eslint-disable-next-line prefer-regex-literals - name: Joi.string().pattern(new RegExp("^[a-zA-Z]{3,30}$")), -}); - -const emailSchema = Joi.object({ - email: Joi.string().email({ - minDomainSegments: 2, - tlds: { allow: ["com", "net"] }, - }), -}); - -const phoneSchema = Joi.object({ - phone: Joi.string().pattern(/^\+?\d{9,30}$/), -}); - -router.post("/", async (req, res, next) => { - const { name, email, phone } = req.body; - - if (!name || name === null) { - res.status(400).json({ message: "missing required name field" }); - } - if (!email || email === null) { - res.status(400).json({ message: "missing required email field" }); - } - - if (!phone || phone === null) { - res.status(400).json({ message: "missing required phone number field" }); - } - - const { error } = nameSchema.validate({ name: name }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - const { error } = emailSchema.validate({ email: email }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - const { error } = phoneSchema.validate({ phone: phone }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - try { - const newContacts = await addContact({ - name: name, - email: email, - phone: phone, - }); - - if (newContacts === "nameIs") { - res.status(409).json({ - message: - "The contact with this name already exists...! Please provide different info ! ", - }); - } else if (newContacts === "emailIs") { - res.status(409).json({ - message: - "This email already exists...! Please provide different info ! ", - }); - } else if (newContacts === "phoneIs") { - res.status(409).json({ - message: - "This phone number already exists...! Please provide different info ! ", - }); - } else { - res.status(201).json({ - status: "success", - code: 201, - message: `Success adding contact: name:${name}, email:${email}, phone:${phone} !`, - data: newContacts, - }); - } - } catch (error) { - res.status(500).json({ - status: "fail", - code: 500, - message: `Error adding contact...!`, - }); - } - } - } - } -}); - -router.delete("/:contactId", async (req, res, next) => { - const id = req.params.contactId; - - try { - const deletedContact = await deleteContact(id); - if (deletedContact === "isNot") { - res.status(404).json({ message: `contact with id:${id} not found` }); - } else { - res.status(200).json({ message: `contact with id:${id} deleted` }); - } - } catch (error) { - res.status(500).json({ - status: "fail", - code: 500, - message: "Error deleting contact...!", - }); - } -}); - -router.put("/:contactId", async (req, res, next) => { - const id = req.params.contactId; - const { name, email, phone } = req.body; - - if (!name && !email && !phone) { - res.status(400).json({ message: "missing fields" }); - return; - } - - try { - const updatedContacts = await patchContact({ - id: id, - name: name, - email: email, - phone: phone, - }); - - if (updatedContacts === false) { - res.status(404).json({ message: "Contact for update is not found...!" }); - } else { - const { error } = nameSchema.validate({ name: name }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - const { error } = emailSchema.validate({ email: email }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - const { error } = phoneSchema.validate({ phone: phone }); - if (error) { - return res.status(400).json({ error: error.details[0].message }); - } else { - res.status(200).json({ - status: "success", - code: 200, - message: `Success updating contact with id:${id}...!`, - data: updatedContacts, - }); - } - } - } - } - } catch (error) { - res.status(500).json({ - status: "fail", - code: 500, - message: "Error updating contact...!", - }); - } -}); +router.patch("/:contactId/favorite", controller.updateFavoriteStatus); module.exports = router; diff --git a/server.js b/server.js index d3923aa3393..824e586a373 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,21 @@ const app = require("./app"); -app.listen(3000, () => { - console.log("Server running. Use our API on port: 3000"); -}); +const mongoose = require("mongoose"); + +require("dotenv").config(); + +const PORT = process.env.PORT; +const uriDB = process.env.DB_URL; + +const connection = mongoose.connect(uriDB); + +connection + .then(() => { + app.listen(PORT, () => { + console.log("Server running. Use our API on port:", PORT); + }); + }) + .catch((err) => { + console.log(`Server not running. Error message: ${err.message}`); + process.exit(1); + }); diff --git a/services/actions.js b/services/actions.js deleted file mode 100644 index c91a9d3d7b0..00000000000 --- a/services/actions.js +++ /dev/null @@ -1,172 +0,0 @@ -const path = require("path"); -const fs = require("fs").promises; // Use the promise-based version of fs - -const contactsPath = path.join(__dirname, "..", "models", "contacts.json"); -// console.log(tasksPath); - -//! list contacts -async function listContacts() { - try { - const data = await fs.readFile(contactsPath, "utf-8"); - const contacts = JSON.parse(data); - - // console.table(contacts); - return contacts; - } catch (error) { - console.log("Error on reading file...!"); - throw error; - } -} - -// listContacts(); - -//! list contact by id -async function listContactById(contactId) { - try { - const data = await fs.readFile(contactsPath, "utf-8"); - const contacts = JSON.parse(data); - - const contact = contacts.find((contact) => contact.id === contactId); - - if (!contact) { - // console.log(`Contact with id ${contactId} does not exists !`); - return false; - } - - // console.table(contact); - return contact; - } catch (error) { - console.log("Error on reading file...!"); - throw error; - } -} - -// listContactById({ id: "AeHIrLTr6JkxGE6SN-0Rw" }); - -//! add contact -async function addContact({ name, email, phone }) { - if (!name) { - // console.log("Please enter name ...!"); - - return; - } - if (!email) { - // console.log("Please enter email ...!"); - - return; - } - - if (!phone) { - // console.log("Please enter phone ...!"); - - return; - } - - try { - const data = await fs.readFile(contactsPath, "utf-8"); - const contacts = JSON.parse(data); - - const isName = contacts.some((contact) => contact.name === name); - - if (isName) { - return "nameIs"; - } - - const isEmail = contacts.some((contact) => contact.email === email); - - if (isEmail) { - return "emailIs"; - } - - const isPhone = contacts.some((contact) => contact.phone === phone); - - if (isPhone) { - return "phoneIs"; - } - - const newContact = { - id: String(Date.now()), - name: name, - email: email, - phone: phone, - }; - - contacts.push(newContact); - - await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2)); - - // console.table(contacts); - return contacts; - } catch (error) { - console.log("Error on reading file...!", error); - throw error; - } -} - -//! delete contact -async function deleteContact(id) { - try { - const data = await fs.readFile(contactsPath, "utf-8"); - const contacts = JSON.parse(data); - - const isId = contacts.some((task) => id === task.id); - if (!isId) { - console.log("This user does not exists...!"); - - return "isNot"; - } - - const newContacts = contacts.filter((contact) => contact.id !== id); - - await fs.writeFile(contactsPath, JSON.stringify(newContacts, null, 2)); - - // console.table(newContacts); - return newContacts; - } catch (error) { - console.log("Error on reading file...!", error); - throw error; - } -} - -//! Inlocuieste resursa partial cu findIndex -async function patchContact({ id, name, email, phone }) { - try { - const data = await fs.readFile(contactsPath, "utf-8"); - const contacts = JSON.parse(data); - // console.log(contacts); - - const contactIndex = contacts.findIndex((contact) => contact.id === id); - - if (contactIndex === -1) { - // console.log("This contact does not exists...!"); - return false; - } - - if (name !== undefined) { - contacts[contactIndex].name = name; - } - - if (email !== undefined) { - contacts[contactIndex].email = email; - } - - if (phone !== undefined) { - contacts[contactIndex].phone = phone; - } - - await fs.writeFile(contactsPath, JSON.stringify(contacts, null, 2)); - - return contacts; - } catch (error) { - console.log("Error on patching file...!", error); - throw error; - } -} - -module.exports = { - listContacts, - listContactById, - addContact, - deleteContact, - patchContact, -}; diff --git a/services/index.js b/services/index.js new file mode 100644 index 00000000000..1b791f585ea --- /dev/null +++ b/services/index.js @@ -0,0 +1,29 @@ +const Contact = require("./schemas/contactsShema"); + +const getAllContacts = async () => { + return Contact.find(); +}; + +const getContactById = async (id) => { + return Contact.findOne({ _id: id }); +}; + +const createContact = async ({ name, email, phone, favorite }) => { + return Contact.create({ name, email, phone, favorite }); +}; + +const updateContact = async (id, fields) => { + return Contact.findByIdAndUpdate({ _id: id }, fields, { new: true }); +}; + +const deleteContact = async (id) => { + return Contact.findByIdAndDelete(id); +}; + +module.exports = { + getAllContacts, + getContactById, + createContact, + updateContact, + deleteContact, +}; From 56991a194f1ccafeebdfa942ddc2e6d2d944ac0f Mon Sep 17 00:00:00 2001 From: Radu Bogdan Naramzoiu Date: Mon, 28 Oct 2024 02:01:27 +0200 Subject: [PATCH 5/5] finished homework 4 --- app.js | 32 +- .../{index.js => ContactsControllers.js} | 166 ++++++++++- controllers/usersControllers.js | 175 +++++++++++ middlewares/authMiddleware.js | 48 +++ middlewares/validationMiddleware.js | 8 + models/UsersShema.js | 37 +++ models/{schemas => }/contactsShema.js | 9 +- models/index.js | 29 -- package-lock.json | 275 +++++++++++++++++- package.json | 6 +- passport/passportConfig.js | 31 ++ readme.es.md | 27 -- readme.md | 31 -- readme.pl.md | 29 -- routes/api/contacts.js | 19 -- routes/api/contactsRoutes.js | 25 ++ routes/api/usersRoutes.js | 13 + services/contactsServices.js | 39 +++ services/index.js | 29 -- services/userServices.js | 90 ++++++ 20 files changed, 924 insertions(+), 194 deletions(-) rename controllers/{index.js => ContactsControllers.js} (50%) create mode 100644 controllers/usersControllers.js create mode 100644 middlewares/authMiddleware.js create mode 100644 middlewares/validationMiddleware.js create mode 100644 models/UsersShema.js rename models/{schemas => }/contactsShema.js (70%) delete mode 100644 models/index.js create mode 100644 passport/passportConfig.js delete mode 100644 readme.es.md delete mode 100644 readme.md delete mode 100644 readme.pl.md delete mode 100644 routes/api/contacts.js create mode 100644 routes/api/contactsRoutes.js create mode 100644 routes/api/usersRoutes.js create mode 100644 services/contactsServices.js delete mode 100644 services/index.js create mode 100644 services/userServices.js diff --git a/app.js b/app.js index 8bfc22435e7..c2ab7814ff0 100644 --- a/app.js +++ b/app.js @@ -1,10 +1,13 @@ const express = require("express"); const logger = require("morgan"); +const passport = require("./passport/passportConfig"); + const cors = require("cors"); const corsOptions = require("./cors"); -const contactsRouter = require("./routes/api/contacts"); +const usersRouter = require("./routes/api/usersRoutes"); +const contactsRouter = require("./routes/api/contactsRoutes"); const app = express(); @@ -14,15 +17,28 @@ app.use(logger(formatsLogger)); app.use(cors(corsOptions)); app.use(express.json()); // app.use(logger("tiny")); - -app.use("/api/contacts", contactsRouter); - -app.use((req, res) => { - res.status(404).json({ message: "Not found" }); +app.use(passport.initialize()); + +app.use("/api", usersRouter); +app.use("/api", contactsRouter); + +app.use((_, res, __) => { + res.status(404).json({ + status: "error", + code: 404, + message: "Use api on routes: /api/users or /api/contacts ", + data: "Not found", + }); }); -app.use((err, req, res, next) => { - res.status(500).json({ message: err.message }); +app.use((err, _, res, __) => { + console.log(err.stack); + res.status(500).json({ + status: "fail", + code: 500, + message: err.message, + data: "Internal Server Error", + }); }); module.exports = app; diff --git a/controllers/index.js b/controllers/ContactsControllers.js similarity index 50% rename from controllers/index.js rename to controllers/ContactsControllers.js index 42308a214f3..cfaefe8b2a6 100644 --- a/controllers/index.js +++ b/controllers/ContactsControllers.js @@ -1,10 +1,41 @@ -const models = require("../models/index"); +const models = require("../services/contactsServices"); + +const jwt = require("jsonwebtoken"); const Joi = require("joi"); +require("dotenv").config(); +const secret = process.env.JWT_SECRET; + const getContacts = async (req, res, next) => { + const { page, limit, favorite } = req.query; + try { - const contacts = await models.listContacts(); + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res + .status(401) + .json({ status: "error", message: "Missing Authorization header" }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + const contacts = await models.getContacts(userId, { + page, + limit, + favorite, + }); res.status(200).json({ status: "success", code: "200", @@ -16,6 +47,7 @@ const getContacts = async (req, res, next) => { status: "fail", code: 500, message: "Contacts listing error...!", + data: error.message, }); next(error); } @@ -26,7 +58,27 @@ const getContactsById = async (req, res, next) => { // console.log(id); try { - const contact = await models.getContactById(id); + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res + .status(401) + .json({ status: "error", message: "Missing Authorization header" }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + const contact = await models.getContactById(userId, id); // console.table(contact); if (!contact) { @@ -85,7 +137,28 @@ const createContact = async (req, res, next) => { return res.status(400).json({ error: error.details[0].message }); } else { try { - const newContact = await models.addContact({ + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res.status(401).json({ + status: "error", + message: "Missing Authorization header", + }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + const newContact = await models.createContact(userId, { name: name, email: email, phone: phone, @@ -112,14 +185,37 @@ const createContact = async (req, res, next) => { }; const deleteContact = async (req, res, next) => { - const id = req.params.contactId; + const contactId = req.params.contactId; try { - const deletedContact = await models.removeContact(id); + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res.status(401).json({ + status: "error", + message: "Missing Authorization header", + }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + const deletedContact = await models.deleteContact(userId, contactId); if (!deletedContact) { - res.status(404).json({ message: `contact with id:${id} not found` }); + res + .status(404) + .json({ message: `contact with id:${contactId} not found` }); } else { - res.status(200).json({ message: `contact with id:${id} deleted` }); + res.status(200).json({ message: `contact with id:${contactId} deleted` }); } } catch (error) { res.status(500).json({ @@ -132,7 +228,7 @@ const deleteContact = async (req, res, next) => { }; const updateContact = async (req, res, next) => { - const id = req.params.contactId; + const contactId = req.params.contactId; const { name, email, phone } = req.body; // Check for at least one field to update @@ -170,7 +266,28 @@ const updateContact = async (req, res, next) => { } try { - const updatedContact = await models.updateContact(id, { + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res.status(401).json({ + status: "error", + message: "Missing Authorization header", + }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + const updatedContact = await models.updateContact(userId, contactId, { ...updatedElements, }); @@ -183,7 +300,7 @@ const updateContact = async (req, res, next) => { res.status(200).json({ status: "success", code: 200, - message: `Success updating contact with id:${id}...!`, + message: `Success updating contact with id:${contactId}...!`, data: updatedContact, }); } catch (error) { @@ -198,13 +315,32 @@ const updateContact = async (req, res, next) => { }; const updateFavoriteStatus = async (req, res, next) => { - const id = req.params.contactId; + const contactId = req.params.contactId; const { favorite = false } = req.body; try { - // console.log(id); + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res.status(401).json({ + status: "error", + message: "Missing Authorization header", + }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; - const result = await models.updateContact(id, { favorite }); + const result = await models.updateContact(userId, contactId, { favorite }); if (result) { res.json({ @@ -216,7 +352,7 @@ const updateFavoriteStatus = async (req, res, next) => { res.status(404).json({ status: "error", code: 404, - message: `Not found Contact id: ${id}`, + message: `Not found Contact id: ${contactId}`, data: "Not Found", }); } diff --git a/controllers/usersControllers.js b/controllers/usersControllers.js new file mode 100644 index 00000000000..9eeadbb2c4e --- /dev/null +++ b/controllers/usersControllers.js @@ -0,0 +1,175 @@ +const jwt = require("jsonwebtoken"); + +const { + registerUser, + getUserById, + loginUser, + logoutUser, + updateSubscription, +} = require("../services/userServices"); + +const { validateUser } = require("../middlewares/validationMiddleware"); + +require("dotenv").config(); +const secret = process.env.JWT_SECRET; + +exports.register = async (req, res, next) => { + const { email, password } = req.body; + + // const { error } = validateUser.validate(req.body); + // if (error) { + // return res.status(400).json({ message: error.message }); + // } + + try { + const newUser = await registerUser(email, password); + console.log(newUser); + + res.status(201).json({ + user: { + email: newUser.email, + subscription: newUser.subscription, + token: newUser.token, + }, + }); + } catch (error) { + res.status(500).json({ message: error.message }); + next(error); + } +}; + +exports.login = async (req, res, next) => { + const { email, password } = req.body; + const { error } = validateUser.validate(req.body); + if (error) { + return res.status(400).json({ message: error.message }); + } + try { + const user = await loginUser(email, password); + + res.status(200).json({ + token: user.token, + user: { + email: user.user.email, + subscription: user.user.subscription, + }, + }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}; + +exports.logout = async (req, res, next) => { + try { + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res + .status(401) + .json({ status: "error", message: "Missing Authorization header" }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + // Continuați cu logica dvs. pentru a găsi utilizatorul și a trimite răspunsul + const result = await logoutUser(userId); + + if (result) { + res.status(204).json({ message: "Logged out", data: result }); + } + } catch (error) { + res.status(500).json({ message: error.message }); + next(error); + } +}; + +exports.getCurrentUser = async (req, res, next) => { + try { + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res + .status(401) + .json({ status: "error", message: "Missing Authorization header" }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const userId = user.id; + + // Continuați cu logica dvs. pentru a găsi utilizatorul și a trimite răspunsul + const result = await getUserById(userId); + // console.log(result); + if (result) { + res.status(200).json({ + status: "success", + code: 200, + data: { email: result.email, subscription: result.subscription }, + }); + } else { + // Returnați o eroare 404 sau 401 în funcție de situație + res.status(404).json({ status: "error", message: "User not found" }); + } + } catch (error) { + console.log(error); + res.status(500).json({ status: "error", message: "Server error" }); + next(error); + } +}; + +exports.updateSubscription = async (req, res) => { + const { subscription } = req.body; + // console.log(subscription); + + try { + const authHeader = req.headers.authorization; + // console.log(authHeader); + + if (!authHeader) { + // Dacă antetul "Authorization" lipsește, returnați o eroare de autentificare + return res + .status(401) + .json({ status: "error", message: "Missing Authorization header" }); + } + + // Extrageți token-ul eliminând prefixul "Bearer " + const token = authHeader.split(" ")[1]; + // console.log(token); + + // Verificați token-ul utilizând cheia secretă + const user = jwt.verify(token, secret); + // console.log(user); + + const { id, email } = user; + // console.log(id); + + // Continuați cu logica dvs. pentru a găsi utilizatorul și a trimite răspunsul + const updatedUser = await updateSubscription(id, { subscription }); + // console.log(updatedUser); + + res.status(200).json({ + email: email, + subscription: updatedUser.subscription, + }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}; diff --git a/middlewares/authMiddleware.js b/middlewares/authMiddleware.js new file mode 100644 index 00000000000..77c3b21949f --- /dev/null +++ b/middlewares/authMiddleware.js @@ -0,0 +1,48 @@ +const jwt = require("jsonwebtoken"); +const User = require("../models/UsersShema"); +require("dotenv").config(); + +const authMiddleware = async (req, res, next) => { + const authHeader = req.headers.authorization; + + if (!authHeader || !authHeader.startsWith("Bearer ")) { + return res.status(401).json({ message: "Not authorized" }); + } + + const token = authHeader.split(" ")[1]; + // console.log(token); + + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + // console.log(decoded); + + const user = await User.findById(decoded.id); + // console.log(user.token === token); + + if (!user || user.token !== token) { + return res.status(401).json({ message: "Not authorized" }); + } + + req.user = user; + next(); + } catch (error) { + return res.status(401).json({ message: "Not authorized" }); + } +}; + +module.exports = { authMiddleware }; + +// const passport = require("passport"); +// const { Unauthorized } = require("http-errors"); + +// const authMiddleware = (req, res, next) => { +// passport.authenticate("jwt", { session: false }, (err, user) => { +// if (err || !user) { +// return next(new Unauthorized("Not authorized")); +// } +// req.user = user; +// next(); +// })(req, res, next); +// }; + +// module.exports = authMiddleware; diff --git a/middlewares/validationMiddleware.js b/middlewares/validationMiddleware.js new file mode 100644 index 00000000000..057fac36426 --- /dev/null +++ b/middlewares/validationMiddleware.js @@ -0,0 +1,8 @@ +const Joi = require("joi"); + +const validateUser = Joi.object({ + email: Joi.string().email().required(), + password: Joi.string().min(6).required(), +}); + +module.exports = { validateUser }; diff --git a/models/UsersShema.js b/models/UsersShema.js new file mode 100644 index 00000000000..83ad8271c53 --- /dev/null +++ b/models/UsersShema.js @@ -0,0 +1,37 @@ +const mongoose = require("mongoose"); +const bcrypt = require("bcryptjs"); + +const userSchema = new mongoose.Schema( + { + email: { + type: String, + required: [true, "Email is required"], + unique: true, + }, + password: { + type: String, + required: [true, "Password is required"], + }, + subscription: { + type: String, + enum: ["starter", "pro", "business"], + default: "starter", + }, + token: { + type: String, + default: null, + }, + }, + { versionKey: false, timestamps: true } +); + +userSchema.methods.setPassword = function (password) { + this.password = bcrypt.hashSync(password, bcrypt.genSaltSync(10)); +}; + +userSchema.methods.validPassword = function (password) { + return bcrypt.compareSync(password, this.password); +}; + +const User = mongoose.model("User", userSchema); +module.exports = User; diff --git a/models/schemas/contactsShema.js b/models/contactsShema.js similarity index 70% rename from models/schemas/contactsShema.js rename to models/contactsShema.js index 471b25ccf49..aed42a3430f 100644 --- a/models/schemas/contactsShema.js +++ b/models/contactsShema.js @@ -1,7 +1,7 @@ const mongoose = require("mongoose"); const Schema = mongoose.Schema; -const contact = new Schema( +const contactSchema = new Schema( { name: { type: String, @@ -20,10 +20,15 @@ const contact = new Schema( type: Boolean, default: false, }, + owner: { + type: Schema.Types.ObjectId, + ref: "User", + required: true, + }, }, { versionKey: false, timestamps: true } ); -const Contact = mongoose.model("contacts", contact); +const Contact = mongoose.model("Contact", contactSchema); module.exports = Contact; diff --git a/models/index.js b/models/index.js deleted file mode 100644 index 76b56c316a1..00000000000 --- a/models/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const Contact = require("./schemas/contactsShema"); - -const listContacts = async () => { - return Contact.find(); -}; - -const getContactById = async (contactId) => { - return Contact.findOne({ _id: contactId }); -}; - -const removeContact = async (contactId) => { - return Contact.findByIdAndDelete(contactId); -}; - -const addContact = async ({ name, email, phone, favorite }) => { - return Contact.create({ name, email, phone, favorite }); -}; - -const updateContact = async (contactId, fields) => { - return Contact.findByIdAndUpdate({ _id: contactId }, fields, { new: true }); -}; - -module.exports = { - listContacts, - getContactById, - removeContact, - addContact, - updateContact, -}; diff --git a/package-lock.json b/package-lock.json index 540406a6a63..6024775c7e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,13 +8,17 @@ "name": "template", "version": "0.0.0", "dependencies": { + "bcryptjs": "^2.4.3", "cors": "2.8.5", "cross-env": "7.0.3", "dotenv": "^16.4.5", "express": "4.17.1", "joi": "^17.13.3", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.1", - "morgan": "1.10.0" + "morgan": "1.10.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1" }, "devDependencies": { "eslint": "7.19.0", @@ -420,6 +424,12 @@ "node": ">= 0.8" } }, + "node_modules/bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", + "license": "MIT" + }, "node_modules/binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -529,6 +539,12 @@ "node": ">=16.20.1" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -934,6 +950,15 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -2351,6 +2376,49 @@ "json5": "lib/cli.js" } }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -2415,6 +2483,48 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -3003,6 +3113,42 @@ "node": ">= 0.8" } }, + "node_modules/passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "license": "MIT", + "dependencies": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + }, + "engines": { + "node": ">= 0.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jaredhanson" + } + }, + "node_modules/passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "license": "MIT", + "dependencies": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "node_modules/passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==", + "engines": { + "node": ">= 0.4.0" + } + }, "node_modules/path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -3040,6 +3186,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "node_modules/pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "node_modules/picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -3326,7 +3477,6 @@ "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4287,6 +4437,11 @@ "safe-buffer": "5.1.2" } }, + "bcryptjs": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", + "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==" + }, "binary-extensions": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", @@ -4373,6 +4528,11 @@ "resolved": "https://registry.npmjs.org/bson/-/bson-6.8.0.tgz", "integrity": "sha512-iOJg8pr7wq2tg/zSlCCHMi3hMm5JTOxLTagf3zxhcenHsFp+c6uOs6K7W5UE7A4QIJGtqh/ZovFNMP4mOPJynQ==" }, + "buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + }, "bytes": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", @@ -4653,6 +4813,14 @@ "integrity": "sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==", "dev": true }, + "ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -5704,6 +5872,42 @@ "minimist": "^1.2.0" } }, + "jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "requires": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + } + }, + "jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "requires": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "requires": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, "kareem": { "version": "2.6.3", "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.6.3.tgz", @@ -5753,6 +5957,41 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, + "lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" + }, + "lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" + }, + "lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" + }, + "lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" + }, + "lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" + }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" + }, "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", @@ -6153,6 +6392,30 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" }, + "passport": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/passport/-/passport-0.7.0.tgz", + "integrity": "sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==", + "requires": { + "passport-strategy": "1.x.x", + "pause": "0.0.1", + "utils-merge": "^1.0.1" + } + }, + "passport-jwt": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.1.tgz", + "integrity": "sha512-UCKMDYhNuGOBE9/9Ycuoyh7vP6jpeTp/+sfMJl7nLff/t6dps+iaeE0hhNkKN8/HZHcJ7lCdOyDxHdDoxoSvdQ==", + "requires": { + "jsonwebtoken": "^9.0.0", + "passport-strategy": "^1.0.0" + } + }, + "passport-strategy": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz", + "integrity": "sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==" + }, "path-exists": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", @@ -6181,6 +6444,11 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" }, + "pause": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", + "integrity": "sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==" + }, "picomatch": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", @@ -6386,8 +6654,7 @@ "semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", - "dev": true + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==" }, "semver-diff": { "version": "3.1.1", diff --git a/package.json b/package.json index 6a18040681e..c308b7af9ab 100644 --- a/package.json +++ b/package.json @@ -9,13 +9,17 @@ "lint:fix": "eslint --fix **/*.js" }, "dependencies": { + "bcryptjs": "^2.4.3", "cors": "2.8.5", "cross-env": "7.0.3", "dotenv": "^16.4.5", "express": "4.17.1", "joi": "^17.13.3", + "jsonwebtoken": "^9.0.2", "mongoose": "^8.7.1", - "morgan": "1.10.0" + "morgan": "1.10.0", + "passport": "^0.7.0", + "passport-jwt": "^4.0.1" }, "devDependencies": { "eslint": "7.19.0", diff --git a/passport/passportConfig.js b/passport/passportConfig.js new file mode 100644 index 00000000000..f8a4ba4357e --- /dev/null +++ b/passport/passportConfig.js @@ -0,0 +1,31 @@ +const passport = require("passport"); +const { Strategy, ExtractJwt } = require("passport-jwt"); +const User = require("../models/UsersShema"); +const dotenv = require("dotenv"); + +dotenv.config(); +const secret = process.env.JWT_SECRET; + +const opts = { + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + secretOrKey: secret, +}; + +passport.use( + new Strategy(opts, async (payload, done) => { + try { + const user = await User.findById(payload.id); + if (!user) { + return done(null, false); + } + if (user.token !== payload.token) { + return done(null, false); + } + return done(null, user); + } catch (error) { + done(error, false); + } + }) +); + +module.exports = passport; diff --git a/readme.es.md b/readme.es.md deleted file mode 100644 index 25cf77f2ef2..00000000000 --- a/readme.es.md +++ /dev/null @@ -1,27 +0,0 @@ -## GoIT Node.js Course Template Homework - -Realice un fork de este repositorio para realizar las tareas (2-6).El fork creará un repositorio en su - -[GitHub: Let’s build from here](https://github.com/) - -Agregue al mentor como colaborador.Para cada tarea, cree su propia rama. - -- hw02 -- hw03 -- hw04 -- hw05 -- hw06 - -Cada nueva rama para la tarea debe crearse desde la rama master. -Después de completar una tarea en su rama, debe hacer una solicitud de extracción (PR). Luego, agregue al mentor para la revisión del código. Solo después de que el mentor apruebe el PR, puede fusionar la rama de la tarea en la rama principal. - -Lea atentamente los comentarios del mentor. Corrija los comentarios y haga un commit en la rama de la tarea. Los cambios se reflejarán automáticamente en el PR después de que envíe el commit con las correcciones a GitHub. Luego, agregue nuevamente al mentor para la revisión del código.- Al entregar una tarea, incluya un enlace al PR. - -- El código JavaScript debe estar limpio y claro, y se debe usar Prettier para el formato. - -### Comandos: - -- `npm start` — Inicia el servidor en modo de producción. -- `npm run start:dev` — Inicia el servidor en modo de desarrollo. -- `npm run lint` — Ejecuta la comprobación del código con eslint. Debe ejecutarse antes de cada PR y corregir todos los errores del linter. -- `npm lint:fix` — Lo mismo que la comprobación del linter, pero con correcciones automáticas de errores simples. \ No newline at end of file diff --git a/readme.md b/readme.md deleted file mode 100644 index 4ecbbd24691..00000000000 --- a/readme.md +++ /dev/null @@ -1,31 +0,0 @@ -## GoIT Node.js Course Template Homework - -Виконайте форк цього репозиторію для виконання домашніх завдань (2-6) -Форк створить репозиторій на вашому http://github.com - -Додайте ментора до колаборації - -Для кожної домашньої роботи створюйте свою гілку. - -- hw02 -- hw03 -- hw04 -- hw05 -- hw06 - -Кожна нова гілка для др повинна робитися з master - -Після того, як ви закінчили виконувати домашнє завдання у своїй гілці, необхідно зробити пулл-реквест (PR). Потім додати ментора для рев'ю коду. Тільки після того, як ментор заапрувить PR, ви можете виконати мердж гілки з домашнім завданням у майстер. - -Уважно читайте коментарі ментора. Виправте зауваження та зробіть коміт у гілці з домашнім завданням. Зміни підтягнуться у PR автоматично після того, як ви відправите коміт з виправленнями на github -Після виправлення знову додайте ментора на рев'ю коду. - -- При здачі домашньої роботи є посилання на PR -- JS-код чистий та зрозумілий, для форматування використовується Prettier - -### Команди: - -- `npm start` — старт сервера в режимі production -- `npm run start:dev` — старт сервера в режимі розробки (development) -- `npm run lint` — запустити виконання перевірки коду з eslint, необхідно виконувати перед кожним PR та виправляти всі помилки лінтера -- `npm lint:fix` — та ж перевірка лінтера, але з автоматичними виправленнями простих помилок diff --git a/readme.pl.md b/readme.pl.md deleted file mode 100644 index 817da777968..00000000000 --- a/readme.pl.md +++ /dev/null @@ -1,29 +0,0 @@ -## GoIT Node.js Course Template Homework - -Wykonaj forka tego repozytorium, aby wykonywać zadania domowe (2-6). Fork utworzy repozytorium na Twoim koncie na http://github.com - -Dodaj mentora jako collaboratora. - -Dla każdego zadania domowego utwórz nową gałąź (branch). - -- hw02 -- hw03 -- hw04 -- hw05 -- hw06 - -Każda nowa gałąź dla zadania powinna być tworzona z gałęzi master. - -Po zakończeniu wykonania zadania domowego na swojej gałęzi, należy zrobić pull request (PR). Następnie dodaj mentora do przeglądu kodu. Dopiero po zatwierdzeniu PR przez mentora możesz scalić gałąź z zadaniem domowym do gałęzi master. - -Uważnie czytaj komentarze mentora. Popraw uwagi i zrób commit na gałęzi z zadaniem domowym. Zmiany automatycznie pojawią się w PR po wysłaniu commitu z poprawkami na GitHub. Po poprawkach ponownie dodaj mentora do przeglądu kodu. - -- Podczas oddawania zadania domowego podaj link do PR. -- Kod JS jest czytelny i zrozumiały, do formatowania używany jest Prettier. - -### Komendy: - -- `npm start` — uruchamia serwer w trybie produkcyjnym -- `npm run start:dev` — uruchamia serwer w trybie deweloperskim (development) -- `npm run lint` — uruchamia sprawdzanie kodu z ESLint, należy wykonać przed każdym PR i poprawić wszystkie błędy lintera -- `npm lint:fix` — to samo co powyższe, ale również automatycznie poprawia proste błędy. \ No newline at end of file diff --git a/routes/api/contacts.js b/routes/api/contacts.js deleted file mode 100644 index 897dd9eb063..00000000000 --- a/routes/api/contacts.js +++ /dev/null @@ -1,19 +0,0 @@ -const express = require("express"); - -const router = express.Router(); - -const controller = require("../../controllers/index"); - -router.get("/", controller.getContacts); - -router.get("/:contactId", controller.getContactsById); - -router.post("/", controller.createContact); - -router.delete("/:contactId", controller.deleteContact); - -router.put("/:contactId", controller.updateContact); - -router.patch("/:contactId/favorite", controller.updateFavoriteStatus); - -module.exports = router; diff --git a/routes/api/contactsRoutes.js b/routes/api/contactsRoutes.js new file mode 100644 index 00000000000..0af49f715c2 --- /dev/null +++ b/routes/api/contactsRoutes.js @@ -0,0 +1,25 @@ +const express = require("express"); +const { authMiddleware } = require("../../middlewares/authMiddleware"); + +const router = express.Router(); + +router.use(authMiddleware); + +const contactController = require("../../controllers/ContactsControllers"); + +router.get("/contacts", contactController.getContacts); + +router.get("/contacts/:contactId", contactController.getContactsById); + +router.post("/contacts", contactController.createContact); + +router.delete("/contacts/:contactId", contactController.deleteContact); + +router.put("/contacts/:contactId", contactController.updateContact); + +router.patch( + "/contacts/:contactId/favorite", + contactController.updateFavoriteStatus +); + +module.exports = router; diff --git a/routes/api/usersRoutes.js b/routes/api/usersRoutes.js new file mode 100644 index 00000000000..4a92ae4f951 --- /dev/null +++ b/routes/api/usersRoutes.js @@ -0,0 +1,13 @@ +const express = require("express"); +const authController = require("../../controllers/usersControllers"); +const { authMiddleware } = require("../../middlewares/authMiddleware"); + +const router = express.Router(); + +router.post("/users/signup", authController.register); +router.post("/users/login", authController.login); +router.post("/users/logout", authMiddleware, authController.logout); +router.get("/users/current", authMiddleware, authController.getCurrentUser); +router.patch("/users", authMiddleware, authController.updateSubscription); + +module.exports = router; diff --git a/services/contactsServices.js b/services/contactsServices.js new file mode 100644 index 00000000000..32794859ae9 --- /dev/null +++ b/services/contactsServices.js @@ -0,0 +1,39 @@ +const Contact = require("../models/contactsShema"); + +exports.getContacts = async (userId) => { + return Contact.find({ owner: userId }); +}; + +exports.getContacts = async (userId, { page = 1, limit = 20, favorite }) => { + const skip = (page - 1) * limit; + const query = { owner: userId }; + + if (favorite !== undefined) { + query.favorite = favorite === "true"; + } + + const contacts = await Contact.find(query).skip(skip).limit(Number(limit)); + const totalContacts = await Contact.countDocuments(query); + + return { contacts, totalContacts, page, limit }; +}; + +exports.getContactById = async (userId, contactId) => { + return Contact.findOne({ _id: contactId, owner: userId }); +}; + +exports.createContact = async (userId, contactData) => { + return Contact.create({ ...contactData, owner: userId }); +}; + +exports.updateContact = async (userId, contactId, contactData) => { + return Contact.findOneAndUpdate( + { _id: contactId, owner: userId }, + contactData, + { new: true } + ); +}; + +exports.deleteContact = async (userId, contactId) => { + return Contact.findOneAndDelete({ _id: contactId, owner: userId }); +}; diff --git a/services/index.js b/services/index.js deleted file mode 100644 index 1b791f585ea..00000000000 --- a/services/index.js +++ /dev/null @@ -1,29 +0,0 @@ -const Contact = require("./schemas/contactsShema"); - -const getAllContacts = async () => { - return Contact.find(); -}; - -const getContactById = async (id) => { - return Contact.findOne({ _id: id }); -}; - -const createContact = async ({ name, email, phone, favorite }) => { - return Contact.create({ name, email, phone, favorite }); -}; - -const updateContact = async (id, fields) => { - return Contact.findByIdAndUpdate({ _id: id }, fields, { new: true }); -}; - -const deleteContact = async (id) => { - return Contact.findByIdAndDelete(id); -}; - -module.exports = { - getAllContacts, - getContactById, - createContact, - updateContact, - deleteContact, -}; diff --git a/services/userServices.js b/services/userServices.js new file mode 100644 index 00000000000..d7517112c0a --- /dev/null +++ b/services/userServices.js @@ -0,0 +1,90 @@ +const User = require("../models/UsersShema"); +const jwt = require("jsonwebtoken"); +require("dotenv").config(); +const secret = process.env.JWT_SECRET; + +// Register a new user +const registerUser = async (email, password) => { + const existingUser = await User.findOne({ email }); + + if (existingUser) { + throw new Error("Email in use"); + } + + const user = new User({ email, password }); + user.setPassword(password); + + const token = jwt.sign({ email }, secret, { expiresIn: "1h" }); + + user.token = token; + + await user.save(); + return user; +}; + +// Login a user +const loginUser = async (email, password) => { + const user = await User.findOne({ email }); + + if (!user || !user.validPassword(password)) { + throw new Error("Email or password is wrong"); + } + + const payload = { id: user._id, email: user.email }; + console.log(payload); + + const token = jwt.sign(payload, secret, { + expiresIn: "1h", + }); + + user.token = token; + await user.save(); + + return { token, user }; +}; + +// Get user by ID +const getUserById = async (userId) => { + return await User.findById(userId); +}; + +// Logout user +const logoutUser = async (userId) => { + const loggedOutUser = await User.findByIdAndUpdate( + userId, + { $set: { token: null } }, // Set the token field to null + { new: true } + ); + + return loggedOutUser; +}; + +// Update user subscription +const updateSubscription = async (userId, { subscription }) => { + const isValidSubscription = (subscription) => { + const validSubscriptions = ["starter", "pro", "business"]; + return validSubscriptions.includes(subscription); + }; + + if (!isValidSubscription(subscription)) { + throw new Error("Invalid subscription type"); + } + + const updatedSubscriptionUser = await User.findByIdAndUpdate( + userId, + { $set: { subscription: subscription } }, + { new: true } + ); + + console.log(updatedSubscriptionUser); + + return updatedSubscriptionUser; +}; + +module.exports = { + registerUser, + loginUser, + getUserById, + logoutUser, + updateSubscription, +};