From 4c58da37f466d89b355662d1aee15f81ecfb8fc6 Mon Sep 17 00:00:00 2001 From: Jane Petrovna Date: Sun, 27 Jun 2021 14:44:09 -0400 Subject: [PATCH] begin impl of todo endpoints --- backend/.prettierrc.yaml | 16 + backend/config.json.example | 2 + backend/package.json | 4 + backend/pnpm-lock.yaml | 1181 ++++++++++++++++++-------- backend/src/db_interface.js | 97 ++- backend/src/index.js | 34 +- backend/src/mail.js | 51 +- backend/src/todo.js | 146 ++++ backend/src/user.js | 183 ++-- frontend/src/Navbar.js | 38 +- frontend/src/modules/Signup/index.js | 161 +++- frontend/src/reducers/login.js | 17 + 12 files changed, 1438 insertions(+), 492 deletions(-) create mode 100644 backend/.prettierrc.yaml create mode 100644 backend/src/todo.js diff --git a/backend/.prettierrc.yaml b/backend/.prettierrc.yaml new file mode 100644 index 0000000..ef5eace --- /dev/null +++ b/backend/.prettierrc.yaml @@ -0,0 +1,16 @@ +arrowParens: 'always' +bracketSpacing: true +endOfLine: 'lf' +htmlWhitespaceSensitivity: 'css' +insertPragma: false +jsxBracketSameLine: true +jsxSingleQuote: true +printWidth: 120 +proseWrap: 'preserve' +quoteProps: 'consistent' +requirePragma: false +semi: true +singleQuote: true +tabWidth: 2 +trailingComma: 'none' +useTabs: false diff --git a/backend/config.json.example b/backend/config.json.example index 5fea072..d3b00c5 100644 --- a/backend/config.json.example +++ b/backend/config.json.example @@ -6,6 +6,8 @@ "db_url": "postgres://postgres:@127.0.0.1/todo", "cert": "", "cert_key": "", + "mail_host": "", + "mail_port": 465, "mail_username": "", "mail_password": "" } \ No newline at end of file diff --git a/backend/package.json b/backend/package.json index 642f2fd..2415daa 100644 --- a/backend/package.json +++ b/backend/package.json @@ -17,8 +17,12 @@ "cookie-parser": "^1.4.5", "cors": "^2.8.5", "express": "^4.17.1", + "express-paginate": "^1.0.2", "nodemailer": "^6.6.1", "pg": "^8.6.0", "sequelize": "^6.6.2" + }, + "devDependencies": { + "sequelize-cli": "^6.2.0" } } \ No newline at end of file diff --git a/backend/pnpm-lock.yaml b/backend/pnpm-lock.yaml index 82b9747..5a124bf 100644 --- a/backend/pnpm-lock.yaml +++ b/backend/pnpm-lock.yaml @@ -1,34 +1,81 @@ +lockfileVersion: 5.3 + +specifiers: + cookie-parser: ^1.4.5 + cors: ^2.8.5 + express: ^4.17.1 + express-paginate: ^1.0.2 + nodemailer: ^6.6.1 + pg: ^8.6.0 + sequelize: ^6.6.2 + sequelize-cli: ^6.2.0 + dependencies: cookie-parser: 1.4.5 cors: 2.8.5 express: 4.17.1 + express-paginate: 1.0.2 nodemailer: 6.6.1 pg: 8.6.0 sequelize: 6.6.2_pg@8.6.0 -lockfileVersion: 5.2 + +devDependencies: + sequelize-cli: 6.2.0 + packages: + /@types/node/15.12.1: + resolution: {integrity: sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw==} dev: false - resolution: - integrity: sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw== + + /abbrev/1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + dev: true + /accepts/1.3.7: + resolution: {integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==} + engines: {node: '>= 0.6'} dependencies: mime-types: 2.1.31 negotiator: 0.6.2 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== + + /ansi-regex/2.1.1: + resolution: {integrity: sha1-w7M6te42DYbg5ijwRorn7yfWVN8=} + engines: {node: '>=0.10.0'} + dev: true + + /ansi-regex/4.1.0: + resolution: {integrity: sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==} + engines: {node: '>=6'} + dev: true + + /ansi-styles/3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + dependencies: + color-convert: 1.9.3 + dev: true + /any-promise/1.3.0: + resolution: {integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8=} dev: false - resolution: - integrity: sha1-q8av7tzqUugJzcA3au0845Y10X8= + /array-flatten/1.1.1: + resolution: {integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=} dev: false - resolution: - integrity: sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= + + /balanced-match/1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + dev: true + + /bluebird/3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + dev: true + /body-parser/1.19.0: + resolution: {integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==} + engines: {node: '>= 0.8'} dependencies: bytes: 3.1.0 content-type: 1.0.4 @@ -41,118 +88,243 @@ packages: raw-body: 2.4.0 type-is: 1.6.18 dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== + + /brace-expansion/1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + dev: true + /buffer-writer/2.0.0: + resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} + engines: {node: '>=4'} dev: false - engines: - node: '>=4' - resolution: - integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw== + /bytes/3.1.0: + resolution: {integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==} + engines: {node: '>= 0.8'} dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== + + /call-bind/1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + dependencies: + function-bind: 1.1.1 + get-intrinsic: 1.1.1 + dev: false + + /camelcase/5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + dev: true + + /cli-color/1.4.0: + resolution: {integrity: sha512-xu6RvQqqrWEo6MPR1eixqGPywhYBHRs653F9jfXB2Hx4jdM/3WxiNE1vppRmxtMIfl16SFYTpYlrnqH/HsK/2w==} + dependencies: + ansi-regex: 2.1.1 + d: 1.0.1 + es5-ext: 0.10.53 + es6-iterator: 2.0.3 + memoizee: 0.4.15 + timers-ext: 0.1.7 + dev: true + + /cliui/5.0.0: + resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==} + dependencies: + string-width: 3.1.0 + strip-ansi: 5.2.0 + wrap-ansi: 5.1.0 + dev: true + + /color-convert/1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + dependencies: + color-name: 1.1.3 + dev: true + + /color-name/1.1.3: + resolution: {integrity: sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=} + dev: true + + /commander/2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + dev: true + + /concat-map/0.0.1: + resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=} + dev: true + + /config-chain/1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + dev: true + /content-disposition/0.5.3: + resolution: {integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==} + engines: {node: '>= 0.6'} dependencies: safe-buffer: 5.1.2 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== + /content-type/1.0.4: + resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== + /cookie-parser/1.4.5: + resolution: {integrity: sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw==} + engines: {node: '>= 0.8.0'} dependencies: cookie: 0.4.0 cookie-signature: 1.0.6 dev: false - engines: - node: '>= 0.8.0' - resolution: - integrity: sha512-f13bPUj/gG/5mDr+xLmSxxDsB9DQiTIfhJS/sqjrmfAWiAN+x2O4i/XguTL9yDZ+/IFDanJ+5x7hC4CXT9Tdzw== + /cookie-signature/1.0.6: + resolution: {integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw=} dev: false - resolution: - integrity: sha1-4wOogrNCzD7oylE6eZmXNNqzriw= + /cookie/0.4.0: + resolution: {integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== + /cors/2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} dependencies: object-assign: 4.1.1 vary: 1.1.2 dev: false - engines: - node: '>= 0.10' - resolution: - integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + + /d/1.0.1: + resolution: {integrity: sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==} + dependencies: + es5-ext: 0.10.53 + type: 1.2.0 + dev: true + /debug/2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} dependencies: ms: 2.0.0 dev: false - resolution: - integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + /debug/4.3.1: - dependencies: - ms: 2.1.2 - dev: false - engines: - node: '>=6.0' + resolution: {integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==} + engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true - resolution: - integrity: sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== + dependencies: + ms: 2.1.2 + dev: false + + /decamelize/1.2.0: + resolution: {integrity: sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=} + engines: {node: '>=0.10.0'} + dev: true + /depd/1.1.2: + resolution: {integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= + /destroy/1.0.4: + resolution: {integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=} dev: false - resolution: - integrity: sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= + /dottie/2.0.2: + resolution: {integrity: sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==} dev: false - resolution: - integrity: sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg== + + /editorconfig/0.15.3: + resolution: {integrity: sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g==} + hasBin: true + dependencies: + commander: 2.20.3 + lru-cache: 4.1.5 + semver: 5.7.1 + sigmund: 1.0.1 + dev: true + /ee-first/1.1.1: + resolution: {integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=} dev: false - resolution: - integrity: sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= + + /emoji-regex/7.0.3: + resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==} + dev: true + /encodeurl/1.0.2: + resolution: {integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=} + engines: {node: '>= 0.8'} dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= + + /es5-ext/0.10.53: + resolution: {integrity: sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==} + dependencies: + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + next-tick: 1.0.0 + dev: true + + /es6-iterator/2.0.3: + resolution: {integrity: sha1-p96IkUGgWpSwhUQDstCg+/qY87c=} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-symbol: 3.1.3 + dev: true + + /es6-symbol/3.1.3: + resolution: {integrity: sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==} + dependencies: + d: 1.0.1 + ext: 1.4.0 + dev: true + + /es6-weak-map/2.0.3: + resolution: {integrity: sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-iterator: 2.0.3 + es6-symbol: 3.1.3 + dev: true + /escape-html/1.0.3: + resolution: {integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=} dev: false - resolution: - integrity: sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= + /etag/1.8.1: + resolution: {integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= + + /event-emitter/0.3.5: + resolution: {integrity: sha1-34xp7vFkeSPHFXuc6DhAYQsCzDk=} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + dev: true + + /express-paginate/1.0.2: + resolution: {integrity: sha512-z0VTaLrsMe4PJFifjJCC4Q11cwrveSOejicYOgFi6RzqUMPd8kIlK95x/xq6g6k6urCI2Fd3gadj3AZ9AGqguw==} + dependencies: + lodash.assign: 4.2.0 + lodash.clone: 4.5.0 + lodash.isobject: 3.0.2 + qs: 6.10.1 + dev: false + /express/4.17.1: + resolution: {integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==} + engines: {node: '>= 0.10.0'} dependencies: accepts: 1.3.7 array-flatten: 1.1.1 @@ -185,11 +357,16 @@ packages: utils-merge: 1.0.1 vary: 1.1.2 dev: false - engines: - node: '>= 0.10.0' - resolution: - integrity: sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== + + /ext/1.4.0: + resolution: {integrity: sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==} + dependencies: + type: 2.5.0 + dev: true + /finalhandler/1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -199,23 +376,82 @@ packages: statuses: 1.5.0 unpipe: 1.0.0 dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + + /find-up/3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + dependencies: + locate-path: 3.0.0 + dev: true + /forwarded/0.2.0: + resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== + /fresh/0.5.2: + resolution: {integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= + + /fs-extra/7.0.1: + resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} + engines: {node: '>=6 <7 || >=8'} + dependencies: + graceful-fs: 4.2.6 + jsonfile: 4.0.0 + universalify: 0.1.2 + dev: true + + /fs.realpath/1.0.0: + resolution: {integrity: sha1-FQStJSMVjKpA20onh8sBQRmU6k8=} + dev: true + + /function-bind/1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + /get-caller-file/2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + dev: true + + /get-intrinsic/1.1.1: + resolution: {integrity: sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==} + dependencies: + function-bind: 1.1.1 + has: 1.0.3 + has-symbols: 1.0.2 + dev: false + + /glob/7.1.7: + resolution: {integrity: sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==} + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.0.4 + once: 1.4.0 + path-is-absolute: 1.0.1 + dev: true + + /graceful-fs/4.2.6: + resolution: {integrity: sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==} + dev: true + + /has-symbols/1.0.2: + resolution: {integrity: sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==} + engines: {node: '>= 0.4'} + dev: false + + /has/1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + dependencies: + function-bind: 1.1.1 + /http-errors/1.7.2: + resolution: {integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==} + engines: {node: '>= 0.6'} dependencies: depd: 1.1.2 inherits: 2.0.3 @@ -223,11 +459,10 @@ packages: statuses: 1.5.0 toidentifier: 1.0.0 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== + /http-errors/1.7.3: + resolution: {integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==} + engines: {node: '>= 0.6'} dependencies: depd: 1.1.2 inherits: 2.0.4 @@ -235,172 +470,308 @@ packages: statuses: 1.5.0 toidentifier: 1.0.0 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== + /iconv-lite/0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} dependencies: safer-buffer: 2.1.2 dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + /inflection/1.12.0: + resolution: {integrity: sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=} + engines: {'0': node >= 0.4.0} dev: false - engines: - '0': node >= 0.4.0 - resolution: - integrity: sha1-ogCTVlbW9fa8TcdQLhrstwMihBY= + + /inflight/1.0.6: + resolution: {integrity: sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=} + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + dev: true + /inherits/2.0.3: + resolution: {integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=} dev: false - resolution: - integrity: sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= + /inherits/2.0.4: - dev: false - resolution: - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + /ini/1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + dev: true + /ipaddr.js/1.9.1: + resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} + engines: {node: '>= 0.10'} dev: false - engines: - node: '>= 0.10' - resolution: - integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== + + /is-core-module/2.4.0: + resolution: {integrity: sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==} + dependencies: + has: 1.0.3 + dev: true + + /is-fullwidth-code-point/2.0.0: + resolution: {integrity: sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=} + engines: {node: '>=4'} + dev: true + + /is-promise/2.2.2: + resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==} + dev: true + + /js-beautify/1.14.0: + resolution: {integrity: sha512-yuck9KirNSCAwyNJbqW+BxJqJ0NLJ4PwBUzQQACl5O3qHMBXVkXb/rD0ilh/Lat/tn88zSZ+CAHOlk0DsY7GuQ==} + engines: {node: '>=10'} + hasBin: true + dependencies: + config-chain: 1.1.13 + editorconfig: 0.15.3 + glob: 7.1.7 + nopt: 5.0.0 + dev: true + + /jsonfile/4.0.0: + resolution: {integrity: sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=} + optionalDependencies: + graceful-fs: 4.2.6 + dev: true + + /locate-path/3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + dependencies: + p-locate: 3.0.0 + path-exists: 3.0.0 + dev: true + + /lodash.assign/4.2.0: + resolution: {integrity: sha1-DZnzzNem0mHRm9rrkkUAXShYCOc=} + dev: false + + /lodash.clone/4.5.0: + resolution: {integrity: sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y=} + dev: false + + /lodash.isobject/3.0.2: + resolution: {integrity: sha1-PI+41bW/S/kK4G4U8qUwpO2TXh0=} + dev: false + /lodash/4.17.21: - dev: false - resolution: - integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + /lru-cache/4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + dependencies: + pseudomap: 1.0.2 + yallist: 2.1.2 + dev: true + /lru-cache/6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} dependencies: yallist: 4.0.0 dev: false - engines: - node: '>=10' - resolution: - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + + /lru-queue/0.1.0: + resolution: {integrity: sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM=} + dependencies: + es5-ext: 0.10.53 + dev: true + /media-typer/0.3.0: + resolution: {integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= + + /memoizee/0.4.15: + resolution: {integrity: sha512-UBWmJpLZd5STPm7PMUlOw/TSy972M+z8gcyQ5veOnSDRREz/0bmpyTfKt3/51DhEBqCZQn1udM/5flcSPYhkdQ==} + dependencies: + d: 1.0.1 + es5-ext: 0.10.53 + es6-weak-map: 2.0.3 + event-emitter: 0.3.5 + is-promise: 2.2.2 + lru-queue: 0.1.0 + next-tick: 1.1.0 + timers-ext: 0.1.7 + dev: true + /merge-descriptors/1.0.1: + resolution: {integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=} dev: false - resolution: - integrity: sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= + /methods/1.1.2: + resolution: {integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= + /mime-db/1.48.0: + resolution: {integrity: sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ== + /mime-types/2.1.31: + resolution: {integrity: sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==} + engines: {node: '>= 0.6'} dependencies: mime-db: 1.48.0 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg== + /mime/1.6.0: - dev: false - engines: - node: '>=4' + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} hasBin: true - resolution: - integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + dev: false + + /minimatch/3.0.4: + resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==} + dependencies: + brace-expansion: 1.1.11 + dev: true + /moment-timezone/0.5.33: + resolution: {integrity: sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w==} dependencies: moment: 2.29.1 dev: false - resolution: - integrity: sha512-PTc2vcT8K9J5/9rDEPe5czSIKgLoGsH8UNpA4qZTVw0Vd/Uz19geE9abbIOQKaAQFcnQ3v5YEXrbSc5BpshH+w== + /moment/2.29.1: + resolution: {integrity: sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ==} dev: false - resolution: - integrity: sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + /ms/2.0.0: + resolution: {integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=} dev: false - resolution: - integrity: sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= + /ms/2.1.1: + resolution: {integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==} dev: false - resolution: - integrity: sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== + /ms/2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: false - resolution: - integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + /negotiator/0.6.2: + resolution: {integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== + + /next-tick/1.0.0: + resolution: {integrity: sha1-yobR/ogoFpsBICCOPchCS524NCw=} + dev: true + + /next-tick/1.1.0: + resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} + dev: true + /nodemailer/6.6.1: + resolution: {integrity: sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg==} + engines: {node: '>=6.0.0'} dev: false - engines: - node: '>=6.0.0' - resolution: - integrity: sha512-1xzFN3gqv+/qJ6YRyxBxfTYstLNt0FCtZaFRvf4Sg9wxNGWbwFmGXVpfSi6ThGK6aRxAo+KjHtYSW8NvCsNSAg== + + /nopt/5.0.0: + resolution: {integrity: sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==} + engines: {node: '>=6'} + hasBin: true + dependencies: + abbrev: 1.1.1 + dev: true + /object-assign/4.1.1: + resolution: {integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=} + engines: {node: '>=0.10.0'} dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= + + /object-inspect/1.10.3: + resolution: {integrity: sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw==} + dev: false + /on-finished/2.3.0: + resolution: {integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=} + engines: {node: '>= 0.8'} dependencies: ee-first: 1.1.1 dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= + + /once/1.4.0: + resolution: {integrity: sha1-WDsap3WWHUsROsF9nFC6753Xa9E=} + dependencies: + wrappy: 1.0.2 + dev: true + + /p-limit/2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + dependencies: + p-try: 2.2.0 + dev: true + + /p-locate/3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + dependencies: + p-limit: 2.3.0 + dev: true + + /p-try/2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + dev: true + /packet-reader/1.0.0: + resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} dev: false - resolution: - integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ== + /parseurl/1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + + /path-exists/3.0.0: + resolution: {integrity: sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=} + engines: {node: '>=4'} + dev: true + + /path-is-absolute/1.0.1: + resolution: {integrity: sha1-F0uSaHNVNP+8es5r9TpanhtcX18=} + engines: {node: '>=0.10.0'} + dev: true + + /path-parse/1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + dev: true + /path-to-regexp/0.1.7: + resolution: {integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=} dev: false - resolution: - integrity: sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= + /pg-connection-string/2.5.0: + resolution: {integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==} dev: false - resolution: - integrity: sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ== + /pg-int8/1.0.1: + resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} + engines: {node: '>=4.0.0'} dev: false - engines: - node: '>=4.0.0' - resolution: - integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw== + /pg-pool/3.3.0_pg@8.6.0: + resolution: {integrity: sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg==} + peerDependencies: + pg: '>=8.0' dependencies: pg: 8.6.0 dev: false - peerDependencies: - pg: '>=8.0' - resolution: - integrity: sha512-0O5huCql8/D6PIRFAlmccjphLYWC+JIzvUhSzXSpGaf+tjTZc4nn+Lr7mLXBbFJfvwbP0ywDv73EiaBsxn7zdg== + /pg-protocol/1.5.0: + resolution: {integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==} dev: false - resolution: - integrity: sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ== + /pg-types/2.2.0: + resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} + engines: {node: '>=4'} dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 @@ -408,11 +779,15 @@ packages: postgres-date: 1.0.7 postgres-interval: 1.2.0 dev: false - engines: - node: '>=4' - resolution: - integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA== + /pg/8.6.0: + resolution: {integrity: sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ==} + engines: {node: '>= 8.0.0'} + peerDependencies: + pg-native: '>=2.0.0' + peerDependenciesMeta: + pg-native: + optional: true dependencies: buffer-writer: 2.0.0 packet-reader: 1.0.0 @@ -422,117 +797,137 @@ packages: pg-types: 2.2.0 pgpass: 1.0.4 dev: false - engines: - node: '>= 8.0.0' - peerDependencies: - pg-native: '>=2.0.0' - peerDependenciesMeta: - pg-native: - optional: true - resolution: - integrity: sha512-qNS9u61lqljTDFvmk/N66EeGq3n6Ujzj0FFyNMGQr6XuEv4tgNTXvJQTfJdcvGit5p5/DWPu+wj920hAJFI+QQ== + /pgpass/1.0.4: + resolution: {integrity: sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w==} dependencies: split2: 3.2.2 dev: false - resolution: - integrity: sha512-YmuA56alyBq7M59vxVBfPJrGSozru8QAdoNlWuW3cz8l+UX3cWge0vTvjKhsSHSJpo3Bom8/Mm6hf0TR5GY0+w== + /postgres-array/2.0.0: + resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} + engines: {node: '>=4'} dev: false - engines: - node: '>=4' - resolution: - integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA== + /postgres-bytea/1.0.0: + resolution: {integrity: sha1-AntTPAqokOJtFy1Hz5zOzFIazTU=} + engines: {node: '>=0.10.0'} dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha1-AntTPAqokOJtFy1Hz5zOzFIazTU= + /postgres-date/1.0.7: + resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} + engines: {node: '>=0.10.0'} dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q== + /postgres-interval/1.2.0: + resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} + engines: {node: '>=0.10.0'} dependencies: xtend: 4.0.2 dev: false - engines: - node: '>=0.10.0' - resolution: - integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ== + + /proto-list/1.2.4: + resolution: {integrity: sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk=} + dev: true + /proxy-addr/2.0.7: + resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} + engines: {node: '>= 0.10'} dependencies: forwarded: 0.2.0 ipaddr.js: 1.9.1 dev: false - engines: - node: '>= 0.10' - resolution: - integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== + + /pseudomap/1.0.2: + resolution: {integrity: sha1-8FKijacOYYkX7wqKw0wa5aaChrM=} + dev: true + + /qs/6.10.1: + resolution: {integrity: sha512-M528Hph6wsSVOBiYUnGf+K/7w0hNshs/duGsNXPUCLH5XAqjEtiPGwNONLV0tBH8NoGb0mvD5JubnUTrujKDTg==} + engines: {node: '>=0.6'} + dependencies: + side-channel: 1.0.4 + dev: false + /qs/6.7.0: + resolution: {integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==} + engines: {node: '>=0.6'} dev: false - engines: - node: '>=0.6' - resolution: - integrity: sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== + /range-parser/1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + /raw-body/2.4.0: + resolution: {integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==} + engines: {node: '>= 0.8'} dependencies: bytes: 3.1.0 http-errors: 1.7.2 iconv-lite: 0.4.24 unpipe: 1.0.0 dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== + /readable-stream/3.6.0: + resolution: {integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==} + engines: {node: '>= 6'} dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 dev: false - engines: - node: '>= 6' - resolution: - integrity: sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + + /require-directory/2.1.1: + resolution: {integrity: sha1-jGStX9MNqxyXbiNE/+f3kqam30I=} + engines: {node: '>=0.10.0'} + dev: true + + /require-main-filename/2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + dev: true + + /resolve/1.20.0: + resolution: {integrity: sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==} + dependencies: + is-core-module: 2.4.0 + path-parse: 1.0.7 + dev: true + /retry-as-promised/3.2.0: + resolution: {integrity: sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==} dependencies: any-promise: 1.3.0 dev: false - resolution: - integrity: sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg== + /safe-buffer/5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} dev: false - resolution: - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== + /safe-buffer/5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} dev: false - resolution: - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + /safer-buffer/2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} dev: false - resolution: - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + + /semver/5.7.1: + resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==} + hasBin: true + dev: true + /semver/7.3.5: + resolution: {integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==} + engines: {node: '>=10'} + hasBin: true dependencies: lru-cache: 6.0.0 dev: false - engines: - node: '>=10' - hasBin: true - resolution: - integrity: sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ== + /send/0.17.1: + resolution: {integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==} + engines: {node: '>= 0.8.0'} dependencies: debug: 2.6.9 depd: 1.1.2 @@ -548,35 +943,29 @@ packages: range-parser: 1.2.1 statuses: 1.5.0 dev: false - engines: - node: '>= 0.8.0' - resolution: - integrity: sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - /sequelize-pool/6.1.0: - dev: false - engines: - node: '>= 10.0.0' - resolution: - integrity: sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg== - /sequelize/6.6.2_pg@8.6.0: + + /sequelize-cli/6.2.0: + resolution: {integrity: sha512-6WQ2x91hg30dUn66mXHnzvHATZ4pyI1GHSNbS/TNN/vRR4BLRSLijadeMgC8zqmKDsL0VqzVVopJWfJakuP++Q==} + engines: {node: '>=10.0.0'} + hasBin: true dependencies: - debug: 4.3.1 - dottie: 2.0.2 - inflection: 1.12.0 + cli-color: 1.4.0 + fs-extra: 7.0.1 + js-beautify: 1.14.0 lodash: 4.17.21 - moment: 2.29.1 - moment-timezone: 0.5.33 - pg: 8.6.0 - retry-as-promised: 3.2.0 - semver: 7.3.5 - sequelize-pool: 6.1.0 - toposort-class: 1.0.1 - uuid: 8.3.2 - validator: 10.11.0 - wkx: 0.5.0 + resolve: 1.20.0 + umzug: 2.3.0 + yargs: 13.3.2 + dev: true + + /sequelize-pool/6.1.0: + resolution: {integrity: sha512-4YwEw3ZgK/tY/so+GfnSgXkdwIJJ1I32uZJztIEgZeAO6HMgj64OzySbWLgxj+tXhZCJnzRfkY9gINw8Ft8ZMg==} + engines: {node: '>= 10.0.0'} dev: false - engines: - node: '>=10.0.0' + + /sequelize/6.6.2_pg@8.6.0: + resolution: {integrity: sha512-H/zrzmTK+tis9PJaSigkuXI57nKBvNCtPQol0yxCvau1iWLzSOuq8t3tMOVeQ+Ep8QH2HoD9/+FCCIAqzUr/BQ==} + engines: {node: '>=10.0.0'} peerDependencies: mariadb: '*' mysql2: '*' @@ -597,113 +986,219 @@ packages: optional: true tedious: optional: true - resolution: - integrity: sha512-H/zrzmTK+tis9PJaSigkuXI57nKBvNCtPQol0yxCvau1iWLzSOuq8t3tMOVeQ+Ep8QH2HoD9/+FCCIAqzUr/BQ== + dependencies: + debug: 4.3.1 + dottie: 2.0.2 + inflection: 1.12.0 + lodash: 4.17.21 + moment: 2.29.1 + moment-timezone: 0.5.33 + pg: 8.6.0 + retry-as-promised: 3.2.0 + semver: 7.3.5 + sequelize-pool: 6.1.0 + toposort-class: 1.0.1 + uuid: 8.3.2 + validator: 10.11.0 + wkx: 0.5.0 + transitivePeerDependencies: + - supports-color + dev: false + /serve-static/1.14.1: + resolution: {integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==} + engines: {node: '>= 0.8.0'} dependencies: encodeurl: 1.0.2 escape-html: 1.0.3 parseurl: 1.3.3 send: 0.17.1 dev: false - engines: - node: '>= 0.8.0' - resolution: - integrity: sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== + + /set-blocking/2.0.0: + resolution: {integrity: sha1-BF+XgtARrppoA93TgrJDkrPYkPc=} + dev: true + /setprototypeof/1.1.1: + resolution: {integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==} dev: false - resolution: - integrity: sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== + + /side-channel/1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + dependencies: + call-bind: 1.0.2 + get-intrinsic: 1.1.1 + object-inspect: 1.10.3 + dev: false + + /sigmund/1.0.1: + resolution: {integrity: sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=} + dev: true + /split2/3.2.2: + resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} dependencies: readable-stream: 3.6.0 dev: false - resolution: - integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + /statuses/1.5.0: + resolution: {integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=} + engines: {node: '>= 0.6'} dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= + + /string-width/3.1.0: + resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==} + engines: {node: '>=6'} + dependencies: + emoji-regex: 7.0.3 + is-fullwidth-code-point: 2.0.0 + strip-ansi: 5.2.0 + dev: true + /string_decoder/1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} dependencies: safe-buffer: 5.2.1 dev: false - resolution: - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + + /strip-ansi/5.2.0: + resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==} + engines: {node: '>=6'} + dependencies: + ansi-regex: 4.1.0 + dev: true + + /timers-ext/0.1.7: + resolution: {integrity: sha512-b85NUNzTSdodShTIbky6ZF02e8STtVVfD+fu4aXXShEELpozH+bCpJLYMPZbsABN2wDH7fJpqIoXxJpzbf0NqQ==} + dependencies: + es5-ext: 0.10.53 + next-tick: 1.1.0 + dev: true + /toidentifier/1.0.0: + resolution: {integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==} + engines: {node: '>=0.6'} dev: false - engines: - node: '>=0.6' - resolution: - integrity: sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== + /toposort-class/1.0.1: + resolution: {integrity: sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=} dev: false - resolution: - integrity: sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg= + /type-is/1.6.18: + resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} + engines: {node: '>= 0.6'} dependencies: media-typer: 0.3.0 mime-types: 2.1.31 dev: false - engines: - node: '>= 0.6' - resolution: - integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + + /type/1.2.0: + resolution: {integrity: sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==} + dev: true + + /type/2.5.0: + resolution: {integrity: sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw==} + dev: true + + /umzug/2.3.0: + resolution: {integrity: sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==} + engines: {node: '>=6.0.0'} + dependencies: + bluebird: 3.7.2 + dev: true + + /universalify/0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + dev: true + /unpipe/1.0.0: + resolution: {integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=} + engines: {node: '>= 0.8'} dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= + /util-deprecate/1.0.2: + resolution: {integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=} dev: false - resolution: - integrity: sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + /utils-merge/1.0.1: + resolution: {integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=} + engines: {node: '>= 0.4.0'} dev: false - engines: - node: '>= 0.4.0' - resolution: - integrity: sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= + /uuid/8.3.2: - dev: false + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} hasBin: true - resolution: - integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== + dev: false + /validator/10.11.0: + resolution: {integrity: sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==} + engines: {node: '>= 0.10'} dev: false - engines: - node: '>= 0.10' - resolution: - integrity: sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw== + /vary/1.1.2: + resolution: {integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=} + engines: {node: '>= 0.8'} dev: false - engines: - node: '>= 0.8' - resolution: - integrity: sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= + + /which-module/2.0.0: + resolution: {integrity: sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=} + dev: true + /wkx/0.5.0: + resolution: {integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==} dependencies: '@types/node': 15.12.1 dev: false - resolution: - integrity: sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg== + + /wrap-ansi/5.1.0: + resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==} + engines: {node: '>=6'} + dependencies: + ansi-styles: 3.2.1 + string-width: 3.1.0 + strip-ansi: 5.2.0 + dev: true + + /wrappy/1.0.2: + resolution: {integrity: sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=} + dev: true + /xtend/4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} dev: false - engines: - node: '>=0.4' - resolution: - integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + + /y18n/4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + dev: true + + /yallist/2.1.2: + resolution: {integrity: sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=} + dev: true + /yallist/4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} dev: false - resolution: - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -specifiers: - cookie-parser: ^1.4.5 - cors: ^2.8.5 - express: ^4.17.1 - nodemailer: ^6.6.1 - pg: ^8.6.0 - sequelize: ^6.6.2 + + /yargs-parser/13.1.2: + resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==} + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: true + + /yargs/13.3.2: + resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==} + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.0 + y18n: 4.0.3 + yargs-parser: 13.1.2 + dev: true diff --git a/backend/src/db_interface.js b/backend/src/db_interface.js index cfdd80a..84e9668 100644 --- a/backend/src/db_interface.js +++ b/backend/src/db_interface.js @@ -1,105 +1,136 @@ -const Sequelize = require("sequelize"); -const Config = require("./config.js"); +const Sequelize = require('sequelize'); +const Config = require('./config.js'); if (!Config.config.db_url) { - console.error("No database url found. please set `db_url` in config.json"); + console.error('No database url found. please set `db_url` in config.json'); process.exit(); } const db = new Sequelize(Config.config.db_url); -const UnverifiedUser = db.define("UnverifiedUser", { +const UnverifiedUser = db.define('UnverifiedUser', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.UUIDV4, allowNull: false, primaryKey: true, - unique: true, + unique: true }, verificationToken: { type: Sequelize.DataTypes.STRING, - allowNull: false, + allowNull: false }, email: { type: Sequelize.DataTypes.STRING, allowNull: false, - unique: true, + unique: true }, password_hash: { type: Sequelize.DataTypes.STRING, - allowNull: true, - }, + allowNull: true + } }); -const User = db.define("User", { +const User = db.define('User', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.UUIDV4, allowNull: false, primaryKey: true, - unique: true, + unique: true }, email: { type: Sequelize.DataTypes.STRING, allowNull: false, - unique: true, + unique: true }, password_hash: { type: Sequelize.DataTypes.STRING, - allowNull: true, - }, + allowNull: true + } }); -const Todo = db.define("Todo", { +const Todo = db.define('Todo', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.UUIDV4, allowNull: false, primaryKey: true, - unique: true, + unique: true + }, + user: { + type: Sequelize.DataTypes.UUID, + allowNull: false }, content: { type: Sequelize.DataTypes.TEXT, - allowNull: false, + allowNull: false }, + tags: { + type: Sequelize.DataTypes.ARRAY(Sequelize.DataTypes.STRING), + allowNull: true + }, + complete: { + type: Sequelize.DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }, + deadline: { + type: Sequelize.DataTypes.DATE, + allowNull: true + } }); -const Tag = db.define("Tag", { +const Grouping = db.define('Grouping', { id: { type: Sequelize.DataTypes.UUID, defaultValue: Sequelize.UUIDV4, allowNull: false, primaryKey: true, - unique: true, + unique: true }, - content: { - type: Sequelize.DataTypes.STRING, - allowNull: false, + complete: { + type: Sequelize.DataTypes.BOOLEAN, + allowNull: true }, + manually_added: { + type: Sequelize.DataTypes.ARRAY(Sequelize.DataTypes.UUID), + allowNull: true + }, + required: { + type: Sequelize.DataTypes.ARRAY(Sequelize.DataTypes.STRING), + allowNull: true + }, + exclusions: { + type: Sequelize.DataTypes.ARRAY(Sequelize.DataTypes.STRING), + allowNull: true + } }); -User.hasMany(Todo); -Todo.hasMany(Tag); - let options = { - alter: false, + alter: false }; if (Config.config.alter_db) { options.alter = true; } - -UnverifiedUser.sync(options); -User.sync(options); -Todo.sync(options); -Tag.sync(options); +let start = async () => { + await UnverifiedUser.sync(options); + await User.sync(options); + await Todo.sync(options); + await Grouping.sync(options); +}; +start(); module.exports = { db: db, constructors: { user: () => { return User.build(); - }, + } }, schemas: { user: User, - }, + unverifiedUser: UnverifiedUser, + todo: Todo, + grouping: Grouping + } }; diff --git a/backend/src/index.js b/backend/src/index.js index 037f036..ed6b230 100644 --- a/backend/src/index.js +++ b/backend/src/index.js @@ -1,19 +1,17 @@ -const http = require("http"); -const https = require("https"); -const cors = require("cors"); -const express = require("express"); -const cookieParser = require("cookie-parser"); -const Config = require("./config.js"); +const http = require('http'); +const https = require('https'); +const cors = require('cors'); +const express = require('express'); +const cookieParser = require('cookie-parser'); +const Config = require('./config.js'); -const UserInterface = require("./user.js"); +const UserInterface = require('./user.js'); +const TodoInterface = require('./todo.js'); let credentials = {}; if (Config.config.https) { - if ( - fs.existsSync(Config.config.cert) && - fs.existsSync(Config.config.cert_key) - ) { + if (fs.existsSync(Config.config.cert) && fs.existsSync(Config.config.cert_key)) { credentials.key = fs.readFileSync(Config.config.cert_key); credentials.cert = fs.readFileSync(Config.config.cert); } @@ -27,7 +25,7 @@ app.use(cookieParser()); // force https app.use((req, res, next) => { if (Config.config.https) { - if (req.headers["x-forwarded-proto"] !== "https") { + if (req.headers['x-forwarded-proto'] !== 'https') { return res.redirect(`https://${req.headers.host}${req.url}`); } } @@ -35,14 +33,15 @@ app.use((req, res, next) => { }); if (!Config.config.secret) { - console.error("No password secret found. please set `secret` in config.json"); + console.error('No password secret found. please set `secret` in config.json'); process.exit(); -} else if (Config.config.https && Config.config.secret == "TEST_SECRET") { - console.error("please do not use the testing secret in production."); +} else if (Config.config.https && Config.config.secret == 'TEST_SECRET') { + console.error('please do not use the testing secret in production.'); process.exit(); } -app.use("/api/user", UserInterface.router); +app.use('/api/user', UserInterface.router); +app.use('/api/todo', TodoInterface.router); // serve static files last // app.use(express.static('./static')); @@ -57,6 +56,5 @@ if (Config.config.https) { server.listen(Config.config.port || 8080); } console.log( - `listening on port ${Config.config.port || 8080}` + - ` with https ${Config.config.https ? "enabled" : "disabled"}` + `listening on port ${Config.config.port || 8080}` + ` with https ${Config.config.https ? 'enabled' : 'disabled'}` ); diff --git a/backend/src/mail.js b/backend/src/mail.js index b57f777..49e57c4 100644 --- a/backend/src/mail.js +++ b/backend/src/mail.js @@ -1,3 +1,52 @@ +const Config = require('./config.js'); const nodemailer = require('nodemailer'); -module.exports = +class Mailer { + sender; + started = false; + mailer; + constructor(host, port, email, password) { + this.mailer = nodemailer.createTransport({ + host: host, + port: port, + secure: true, + auth: { + user: email, + pass: password + } + }); + this.sender = email; + this.started = true; + } + async sendMail(recipients, subject, content, contentStripped) { + console.log(`sending mail to ${recipients}`); + let info = await this.mailer.sendMail({ + from: `"Todo App" <${this.sender}>`, + to: Array.isArray(recipients) ? recipients.join(', ') : recipients, + subject: subject, + text: contentStripped, + html: content + }); + } +} + +if (!global.mailer || !global.mailer.started) { + if ( + !Config.config['mail_host'] || + !Config.config['mail_port'] || + !Config.config['mail_username'] || + !Config.config['mail_password'] + ) { + console.error(`could not create email account as +mail_host, mail_port, mail_username or mail_password is not set.`); + process.exit(); + } + global.mailer = new Mailer( + Config.config['mail_host'], + Config.config['mail_port'], + Config.config['mail_username'], + Config.config['mail_password'] + ); +} + +module.exports = global.mailer; diff --git a/backend/src/todo.js b/backend/src/todo.js new file mode 100644 index 0000000..bc31195 --- /dev/null +++ b/backend/src/todo.js @@ -0,0 +1,146 @@ +const express = require('express'); +const paginate = require('express-paginate'); +const Database = require('./db_interface.js'); +const User = require('./user.js'); +const { Op } = require('sequelize'); + +let router = express.Router(); + +router.use(express.json()); + +function map_todo(result) { + return { + id: result.id, + content: result.content, + tags: result.tags + }; +} + +function parse_tags(tags) { + result = { + complete: undefined, + required: [], + excluded: [] + }; + tags.map((tag) => { + if (tag === 'complete') { + complete = true; + } else if (tag === '~complete') { + complete = false; + } else if (tag.startsWith('~')) { + excluded.push(tag); + } else { + required.push(tag); + } + }); + return result; +} + +const todo_fields = ['currentPage', 'limit']; + +router.use(paginate.middleware(10, 50)); +router.use('/todos', User.enforce_session_login); +router.get('/todos', async (req, res) => { + if (!req.query) { + return res.status(400).json({ + error: `query must include the fields: ${todo_fields.join(', ')}}` + }); + } else { + let error = []; + for (let field of todo_fields) { + if (!req.query[field]) { + error.push(field); + } + } + if (error.length > 0) { + return res.status(400).json({ + error: `query must include the fields: ${error.join(', ')}}` + }); + } + } + let tag_options = {}; + if (req.query.tags) { + let parsed = parse_tags(req.query.tags.split(',')); + tag_options['tags'] = { + [Op.and]: parsed.required, + [Op.not]: parsed.excluded + }; + if (parsed.complete !== undefined) { + tag_options['complete'] = { + [Op.is]: parsed.complete + }; + } + } + console.log(tag_options); + let all_todos = await Database.schemas.todo.findAndCountAll({ + where: { + user: req.get('id'), + ...tag_options + }, + limit: req.query.limit, + offset: req.skip + }); + const item_count = all_todos.count; + const page_count = Math.ceil(item_count / req.query.limit); + res.json({ + result: all_todos.map(map_todo), + currentPage: req.query.currentPage, + pageCount: page_count, + itemCount: item_count, + pages: paginate.getArrayPages(req)(5, page_count, req.query.currentPage) + }); +}); + +router.use('/todo', User.enforce_session_login); +router.get('/todo/:id([a-f0-9-]+)', async (req, res) => { + let userid = req.get('id'); + let id = req.params?.id; + + let match = await Database.schemas.todo.findOne({ + where: { + user: userid, + id: id + } + }); + if (!match) { + return res.sendStatus(404); + } + + return res.json({ + result: map_todo(match), + tags: get_tags(match.id) + }); +}); + +router.use('/todo', User.enforce_session_login); +router.post('/todo/:id([a-f0-9-]+)', async (req, res) => { + let userid = req.get('id'); + let id = req.params?.id; + + let body = req.body; + + if (!body) { + return res.sendStatus(400); + } + + let match = await Database.schemas.todo.findOne({ + where: { + user: userid, + id: id + } + }); + if (!match) { + return res.sendStatus(404); + } + + // + + return res.json({ + result: map_todo(match), + tags: get_tags(match.id) + }); +}); + +module.exports = { + router: router +}; diff --git a/backend/src/user.js b/backend/src/user.js index 48d0da8..2968380 100644 --- a/backend/src/user.js +++ b/backend/src/user.js @@ -1,7 +1,7 @@ -const express = require("express"); -const crypto = require("crypto"); -const Config = require("./config.js"); -const Database = require("./db_interface.js"); +const express = require('express'); +const crypto = require('crypto'); +const Config = require('./config.js'); +const Database = require('./db_interface.js'); const Mail = require('./mail.js'); let router = express.Router(); @@ -14,19 +14,19 @@ user_cache = {}; email_cache = {}; async function get_user_details(id) { - if (!id || id === "undefined") { + if (!id || id === 'undefined') { return undefined; } console.log(`search for user with id ${id}`); if (!user_cache[id]) { let user = await Database.schemas.user.findOne({ where: { id: id } }); - if (!user) { + if (user === null) { return undefined; } user_cache[user.id] = { id: user.id, email: user.email, - password_hash: user.password_hash, + password_hash: user.password_hash }; email_cache[user.email] = user.id; } @@ -35,19 +35,19 @@ async function get_user_details(id) { } async function get_user_details_by_email(email) { - if (!email || email === "undefined") { + if (!email || email === 'undefined') { return undefined; } console.log(`search for user with email ${email}}`); if (!email_cache[email] || !user_cache[email_cache[email]]) { let user = await Database.schemas.user.findOne({ where: { email: email } }); - if (!user) { + if (user === null) { return undefined; } user_cache[user.id] = { id: user.id, email: user.email, - password_hash: user.password_hash, + password_hash: user.password_hash }; email_cache[user.email] = user.id; } @@ -55,34 +55,34 @@ async function get_user_details_by_email(email) { return user_cache[email_cache[email]]; } -router.get("/byEmail/:email", async (req, res) => { +router.get('/byEmail/:email', async (req, res) => { if (!req.params?.email) { res.status(400).json({ - error: "email is a required parameter", + error: 'email is a required parameter' }); } let user = get_user_details_by_email(req.params.email); console.log(user); - if (user != null) { + if (user !== undefined && user !== {}) { res.json({ id: user.id, - email: user.email, + email: user.email }); } else { res.sendStatus(404); } }); -function hash(secret, password) { +function hash(secret, password, base64 = true) { let pw_hash = crypto.pbkdf2Sync( password, secret, Config.config.key?.iterations || 1000, Config.config.key?.length || 64, - "sha512" + 'sha512' ); - return pw_hash.toString("base64"); + return pw_hash.toString(base64 ? 'base64' : 'hex'); } function verify(secret, password, hash) { @@ -91,10 +91,10 @@ function verify(secret, password, hash) { secret, Config.config.key?.iterations || 1000, Config.config.key?.length || 64, - "sha512" + 'sha512' ); - return hash === pw_hash.toString("base64"); + return hash === pw_hash.toString('base64'); } function hash_password(password) { @@ -105,9 +105,9 @@ function verify_password(password, hash) { return verify(Config.config.secret, password, hash); } -function get_session_token(id, password_hash) { +function get_session_token(id, password_hash, base64 = true) { session_entropy[id] = crypto.randomBytes(Config.config.session_entropy || 32); - return hash(session_entropy[id], password_hash); + return hash(session_entropy[id], password_hash, base64); } function verify_session_token(id, hash, token) { @@ -119,9 +119,9 @@ function verify_session_token(id, hash, token) { } async function enforce_session_login(req, res, next) { - let userid = req.get("id"); - let session_token = req.get("authorization"); - console.log("a", userid, session_token); + let userid = req.get('id'); + let session_token = req.get('authorization'); + console.log('a', userid, session_token); if (!userid || !session_token) { return res.sendStatus(401); } @@ -129,104 +129,125 @@ async function enforce_session_login(req, res, next) { if (!user) { return res.sendStatus(401); } - let verified_session = verify_session_token( - userid, - user.password_hash, - session_token - ); + let verified_session = verify_session_token(userid, user.password_hash, session_token); if (!verified_session) { return res.sendStatus(401); } return next(); } -router.post("/signup", async (res, req) => { +router.post('/signup', async (req, res) => { if (!req.body?.email || !req.body?.password) { return res.status(400).json({ - error: "must have email and password fields", + error: 'must have email and password fields' }); } - let user = find_user_by_email(req.body?.email); + let user = await get_user_details_by_email(req.body?.email); - if (user != null) { + if (user !== undefined && user !== {}) { + console.warn(`user already found: ${JSON.stringify(user)}`); return res.status(403).json({ - error: `email ${req.body.email} is already in use.`, + error: `email ${req.body.email} is already in use.` }); - } else { - let randomString = "Signup" - for (let i = 0; i < 16, i++) { - randomString += Math.floor(Math.random() * 10) - } + } else { + let match = await Database.schemas.unverifiedUser.findOne({ where: { email: req.body.email } }); + if (!!match) { + await Database.schemas.unverifiedUser.destroy({ where: { email: match.email } }); + } + let randomString = 'Signup'; + for (let i = 0; i < 16; i++) { + randomString += Math.floor(Math.random() * 10); + } + let password_hash = hash_password(req.body.password); let user = await Database.schemas.unverifiedUser.create({ email: String(req.body.email), - password_hash: hash_password(req.body.password), - verificationToken: get_session_token(randomString, ) + password_hash: password_hash, + verificationToken: get_session_token(randomString, password_hash, false) }); - + const link = `${Config.config.https ? 'https://' : 'http://'}${req.headers.host}/api/user/verify?verification=${ + user.verificationToken + }`; + const content = `Click here to verify your sign-up: +${link}`; + const contentHtml = `

Click here to verify your sign-up:

+

${link}

`; + await Mail.sendMail([String(req.body.email)], 'Verify Your Account', contentHtml, content); return res.sendStatus(204); } -}) +}); -router.post("/verify", async (req, res) => { - if (!req.params?.verification) { +router.get('/verify', async (req, res) => { + if (!req.query?.verification) { return res.status(400).send( ` + +

No Verification Link

+ + ` + ); + } + let verification = req.query?.verification; + let user = await Database.schemas.unverifiedUser.findOne({ where: { verificationToken: verification } }); + + if (user !== undefined && user !== {}) { + if (user.verificationToken != verification) { + return res.status(404).send( + `

Unknown Verification Link

` - )); - } -let verification = -let user = await Database.schemas.unverifiedUser.findOne({ where: { id: id } }); - -if (user != null) { + ); + } let newUser = await Database.schemas.user.create({ email: user.email, - password_hash: user.password_hash, + password_hash: user.password_hash }); - return res.json({ - id: user.id, - email: user.email, - }); + return res.send(` + +

Sign up complete.

+ + `); } else { - return res.status(403).json({ - error: `email ${req.body.email} is already in use.`, - }); + return res.status(404).send(` + +

Unknown Verification Link

+ + `); } }); -router.post("/login", async (req, res) => { +router.post('/login', async (req, res) => { if (!req.body?.email || !req.body?.password) { return res.status(400).json({ - error: "must have email and password fields", + error: 'must have email and password fields' }); } let user = await get_user_details_by_email(req.body.email); if (!user) { return res.status(401).json({ - error: "incorrect email or password", + error: 'incorrect email or password' }); } let verified = verify_password(req.body.password, user.password_hash); if (!verified) { return res.status(401).json({ - error: "incorrect email or password", + error: 'incorrect email or password' }); } return res.json({ userid: user.id, - session_token: get_session_token(user.id, user.password_hash), + session_token: get_session_token(user.id, user.password_hash) }); }); -router.post("/logout", async (req, res) => { +router.post('/logout', async (req, res) => { if (!req.body?.session_token || !req.body?.userid) { return res.status(400).json({ - error: "must include user id and session token to log out", + error: 'must include user id and session token to log out' }); } @@ -236,18 +257,14 @@ router.post("/logout", async (req, res) => { let user = await get_user_details(userid); if (!user) { return res.status(401).json({ - error: "invalid user data", + error: 'invalid user data' }); } - let verified = verify_session_token( - user.id, - user.password_hash, - session_token - ); + let verified = verify_session_token(user.id, user.password_hash, session_token); if (!verified) { return res.status(401).json({ - error: "invalid user data", + error: 'invalid user data' }); } @@ -255,41 +272,43 @@ router.post("/logout", async (req, res) => { return res.sendStatus(204); }); -router.get("/:id([a-f0-9-]+)", async (req, res) => { +router.get('/:id([a-f0-9-]+)', async (req, res) => { console.log(req.params); if (!req.params?.id) { return res.status(400).json({ - error: "must have id parameter", + error: 'must have id parameter' }); } let id = req.params?.id; console.log(id); let user = await get_user_details(id); console.log(user); - if (user != null) { + if (user !== undefined && user !== {}) { return res.json({ id: user.id, - email: user.email, + email: user.email }); } else { return res.sendStatus(404); } }); -router.use("/authorized", enforce_session_login); -router.get("/authorized", async (req, res) => { - let userid = req.get("id"); +router.use('/authorized', enforce_session_login); +router.get('/authorized', async (req, res) => { + let userid = req.get('id'); let user = await get_user_details(userid); return res.json({ authorized: true, user: { id: user.id, - email: user.email, - }, + email: user.email + } }); }); module.exports = { router: router, enforce_session_login: enforce_session_login, + get_user_details: get_user_details, + get_user_details_by_email: get_user_details_by_email }; diff --git a/frontend/src/Navbar.js b/frontend/src/Navbar.js index be54659..109af8f 100644 --- a/frontend/src/Navbar.js +++ b/frontend/src/Navbar.js @@ -1,6 +1,7 @@ -import Link from '@material-ui/core/Link'; import Grid from '@material-ui/core/Grid'; import GroupIcon from '@material-ui/icons/Group'; +import SettingsIcon from '@material-ui/icons/Settings'; +import AssignmentIndIcon from '@material-ui/icons/AssignmentInd'; import Box from '@material-ui/core/Box'; import Typography from '@material-ui/core/Typography'; import Button from '@material-ui/core/Button'; @@ -67,7 +68,7 @@ const LoginLogoutButton = connect( window.location.pathname = '/account'; }} className={classes.button}> - + Settings + <> + + + ); }); diff --git a/frontend/src/modules/Signup/index.js b/frontend/src/modules/Signup/index.js index d890acc..e5402a5 100644 --- a/frontend/src/modules/Signup/index.js +++ b/frontend/src/modules/Signup/index.js @@ -1,5 +1,162 @@ +import { signup } from '../../reducers/login'; +import { useState } from 'react'; +import Button from '@material-ui/core/Button'; +import Grid from '@material-ui/core/Grid'; +import InputAdornment from '@material-ui/core/InputAdornment'; +import IconButton from '@material-ui/core/IconButton'; +import TextField from '@material-ui/core/TextField'; +import Typography from '@material-ui/core/Typography'; +import Visibility from '@material-ui/icons/Visibility'; +import VisibilityOff from '@material-ui/icons/VisibilityOff'; +import { connect } from 'react-redux'; +import { withStyles } from '@material-ui/styles'; + +const styles = (theme) => {}; + const SignupPage = (props) => { - return
; + const { classes } = props; + const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [confirmPassword, setConfirmPassword] = useState(''); + const [visible, setVisible] = useState(false); + const [error, setError] = useState(false); + const checkSignup = () => { + if (password !== confirmPassword) { + setError(true); + } else { + setError(false); + } + }; + + return ( + + +
+
+ + Sign Up + + { + return setEmail(event.target.value ?? ''); + }} + onKeyPress={(event) => { + if (event.key === 'Enter') { + checkSignup(); + if (!error) { + props.signup(email, password); + } + } + }} + /> +
+ { + setPassword(event.target.value ?? ''); + }} + onKeyPress={(event) => { + if (event.key === 'Enter') { + checkSignup(); + if (!error) { + props.signup(email, password); + } + } + }} + InputProps={{ + endAdornment: ( + + { + return setVisible(!visible); + }} + edge='end'> + {visible ? : } + + + ) + }} + /> +
+
+ { + setConfirmPassword(event.target.value ?? ''); + }} + onKeyPress={(event) => { + checkSignup(); + if (event.key === 'Enter') { + if (!error) { + props.signup(email, password); + } + } + }} + InputProps={{ + endAdornment: ( + + { + return setVisible(!visible); + }} + edge='end'> + {visible ? : } + + + ) + }} + /> +
+ +
+
+
+
+ ); }; -export default SignupPage; +export default connect( + (state) => { + return {}; + }, + (dispatch, props) => { + return { + signup: (email, password) => { + dispatch(signup(email, password)); + } + }; + } +)(withStyles(styles)(SignupPage)); diff --git a/frontend/src/reducers/login.js b/frontend/src/reducers/login.js index 0eaf01e..79e3ea3 100644 --- a/frontend/src/reducers/login.js +++ b/frontend/src/reducers/login.js @@ -54,6 +54,23 @@ export const login = createAsyncAction((dispatch, getState, config, email, passw ); }); +export const signup = createAsyncAction((dispatch, getState, config, email, password) => { + axios + .post(`${config.apiUrl}/user/signup`, { + email: email, + password: password + }) + .then( + (success) => { + console.error('success', success); + window.location.pathname = '/login'; + }, + (reject) => { + console.error(reject); + } + ); +}); + export const forgotPassword = createAsyncAction((dispatch, getState, config, email) => {}); export const logout = createAsyncAction((dispatch, getState, config) => {