From f65bf959b1bdc392725b03338ce6dc81cbcbd89d Mon Sep 17 00:00:00 2001 From: Mike Smith <89040888+smiggiddy@users.noreply.github.com> Date: Mon, 24 Mar 2025 04:06:46 -0400 Subject: [PATCH] rename the app --- messages/.dockerignore | 15 + messages/Dockerfile | 31 ++ messages/bun.lock | 468 ++++++++++++++++++++ messages/package.json | 11 + messages/public/css/style.css | 428 ++++++++++++++++++ messages/public/images/favicon.png | Bin 0 -> 1240 bytes messages/public/js/script.js | 109 +++++ messages/src/app.js | 53 +++ messages/src/config.js | 0 messages/src/controllers/authController.js | 46 ++ messages/src/controllers/indexController.js | 90 ++++ messages/src/middlewares/auth.js | 50 +++ messages/src/middlewares/logging.js | 8 + messages/src/models/db.js | 35 ++ messages/src/models/init.sql | 73 +++ messages/src/models/query.js | 138 ++++++ messages/src/routes/authRouter.js | 32 ++ messages/src/routes/indexRouter.js | 13 + messages/src/views/content.ejs | 0 messages/src/views/feed.ejs | 31 ++ messages/src/views/footer.ejs | 12 + messages/src/views/header.ejs | 36 ++ messages/src/views/home.ejs | 49 ++ messages/src/views/login.ejs | 30 ++ messages/src/views/note-form.ejs | 19 + messages/src/views/register.ejs | 35 ++ messages/src/views/view-profile.ejs | 20 + 27 files changed, 1832 insertions(+) create mode 100644 messages/.dockerignore create mode 100644 messages/Dockerfile create mode 100644 messages/bun.lock create mode 100644 messages/package.json create mode 100644 messages/public/css/style.css create mode 100644 messages/public/images/favicon.png create mode 100644 messages/public/js/script.js create mode 100644 messages/src/app.js create mode 100644 messages/src/config.js create mode 100644 messages/src/controllers/authController.js create mode 100644 messages/src/controllers/indexController.js create mode 100644 messages/src/middlewares/auth.js create mode 100644 messages/src/middlewares/logging.js create mode 100644 messages/src/models/db.js create mode 100644 messages/src/models/init.sql create mode 100644 messages/src/models/query.js create mode 100644 messages/src/routes/authRouter.js create mode 100644 messages/src/routes/indexRouter.js create mode 100644 messages/src/views/content.ejs create mode 100644 messages/src/views/feed.ejs create mode 100644 messages/src/views/footer.ejs create mode 100644 messages/src/views/header.ejs create mode 100644 messages/src/views/home.ejs create mode 100644 messages/src/views/login.ejs create mode 100644 messages/src/views/note-form.ejs create mode 100644 messages/src/views/register.ejs create mode 100644 messages/src/views/view-profile.ejs diff --git a/messages/.dockerignore b/messages/.dockerignore new file mode 100644 index 0000000..f965aed --- /dev/null +++ b/messages/.dockerignore @@ -0,0 +1,15 @@ +node_modules +Dockerfile* +docker-compose* +.dockerignore +.git +.gitignore +README.md +LICENSE +.vscode +Makefile +helm-charts +.env +.editorconfig +.idea +coverage* diff --git a/messages/Dockerfile b/messages/Dockerfile new file mode 100644 index 0000000..d0631b9 --- /dev/null +++ b/messages/Dockerfile @@ -0,0 +1,31 @@ +FROM oven/bun:1 AS base +WORKDIR /usr/src/app + +# install dependencies into temp directory +# this will cache them and speed up future builds +FROM base AS install +RUN mkdir -p /temp/dev +COPY package.json bun.lock /temp/dev/ +RUN cd /temp/dev && bun install --frozen-lockfile + +# copy node_modules from temp directory +# then copy all (non-ignored) project files into the image +FROM base AS prerelease +COPY --from=install /temp/dev/node_modules node_modules +COPY . . + +# [optional] tests & build +ENV NODE_ENV=production +RUN bun test +RUN bun run build + +# copy production dependencies and source code into final image +FROM base AS release +COPY --from=install /temp/prod/node_modules node_modules +COPY --from=prerelease /usr/src/app/app.js . +COPY --from=prerelease /usr/src/app/package.json . + +# run the app +USER bun +EXPOSE 3000/tcp +ENTRYPOINT [ "bun", "run", "app.js" ] diff --git a/messages/bun.lock b/messages/bun.lock new file mode 100644 index 0000000..7c42a58 --- /dev/null +++ b/messages/bun.lock @@ -0,0 +1,468 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "KeyNotes.App", + "dependencies": { + "connect-sqlite3": "^0.9.15", + "ejs": "^3.1.10", + "express": "^4.21.2", + "express-session": "^1.18.1", + "express-validator": "^7.2.1", + }, + }, + }, + "packages": { + "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="], + + "@npmcli/fs": ["@npmcli/fs@1.1.1", "", { "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" } }, "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ=="], + + "@npmcli/move-file": ["@npmcli/move-file@1.1.2", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg=="], + + "@tootallnate/once": ["@tootallnate/once@1.1.2", "", {}, "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw=="], + + "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], + + "accepts": ["accepts@1.3.8", "", { "dependencies": { "mime-types": "~2.1.34", "negotiator": "0.6.3" } }, "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw=="], + + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + + "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], + + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "aproba": ["aproba@2.0.0", "", {}, "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ=="], + + "are-we-there-yet": ["are-we-there-yet@3.0.1", "", { "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" } }, "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg=="], + + "array-flatten": ["array-flatten@1.1.1", "", {}, "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="], + + "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="], + + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + + "body-parser": ["body-parser@1.20.3", "", { "dependencies": { "bytes": "3.1.2", "content-type": "~1.0.5", "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "on-finished": "2.4.1", "qs": "6.13.0", "raw-body": "2.5.2", "type-is": "~1.6.18", "unpipe": "1.0.0" } }, "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g=="], + + "brace-expansion": ["brace-expansion@1.1.11", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="], + + "buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + + "cacache": ["cacache@15.3.0", "", { "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "glob": "^7.1.4", "infer-owner": "^1.0.4", "lru-cache": "^6.0.0", "minipass": "^3.1.1", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.2", "mkdirp": "^1.0.3", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^8.0.1", "tar": "^6.0.2", "unique-filename": "^1.1.1" } }, "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g=="], + + "call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="], + + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + + "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + + "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "color-support": ["color-support@1.1.3", "", { "bin": { "color-support": "bin.js" } }, "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg=="], + + "concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="], + + "connect-sqlite3": ["connect-sqlite3@0.9.15", "", { "dependencies": { "sqlite3": "^5.0.2" } }, "sha512-aJGDtASX8DTUZ++7iTN97vR0vGFpm8jDFew/qHK3veISkCpVpPS0tMdqs7i9fiHLaqaU0Jh3c4sUvNxsizaSTA=="], + + "console-control-strings": ["console-control-strings@1.1.0", "", {}, "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ=="], + + "content-disposition": ["content-disposition@0.5.4", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], + + "cookie-signature": ["cookie-signature@1.0.6", "", {}, "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="], + + "debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], + + "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], + + "delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="], + + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + + "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], + + "detect-libc": ["detect-libc@2.0.3", "", {}, "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + + "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], + + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + + "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + + "end-of-stream": ["end-of-stream@1.4.4", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="], + + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + + "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + + "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], + + "express-session": ["express-session@1.18.1", "", { "dependencies": { "cookie": "0.7.2", "cookie-signature": "1.0.7", "debug": "2.6.9", "depd": "~2.0.0", "on-headers": "~1.0.2", "parseurl": "~1.3.3", "safe-buffer": "5.2.1", "uid-safe": "~2.1.5" } }, "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA=="], + + "express-validator": ["express-validator@7.2.1", "", { "dependencies": { "lodash": "^4.17.21", "validator": "~13.12.0" } }, "sha512-CjNE6aakfpuwGaHQZ3m8ltCG2Qvivd7RHtVMS/6nVxOM7xVGqr4bhflsm4+N5FP5zI7Zxp+Hae+9RE+o8e3ZOQ=="], + + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + + "filelist": ["filelist@1.0.4", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q=="], + + "finalhandler": ["finalhandler@1.3.1", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ=="], + + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], + + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + + "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "gauge": ["gauge@4.0.4", "", { "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.3", "console-control-strings": "^1.1.0", "has-unicode": "^2.0.1", "signal-exit": "^3.0.7", "string-width": "^4.2.3", "strip-ansi": "^6.0.1", "wide-align": "^1.1.5" } }, "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg=="], + + "get-intrinsic": ["get-intrinsic@1.2.7", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", "get-proto": "^1.0.0", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + + "glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-unicode": ["has-unicode@2.0.1", "", {}, "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "http-cache-semantics": ["http-cache-semantics@4.1.1", "", {}, "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ=="], + + "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], + + "http-proxy-agent": ["http-proxy-agent@4.0.1", "", { "dependencies": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" } }, "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg=="], + + "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + + "iconv-lite": ["iconv-lite@0.4.24", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA=="], + + "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], + + "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + + "infer-owner": ["infer-owner@1.0.4", "", {}, "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + + "ip-address": ["ip-address@9.0.5", "", { "dependencies": { "jsbn": "1.1.0", "sprintf-js": "^1.1.3" } }, "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jake": ["jake@10.9.2", "", { "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", "filelist": "^1.0.4", "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" } }, "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA=="], + + "jsbn": ["jsbn@1.1.0", "", {}, "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="], + + "lodash": ["lodash@4.17.21", "", {}, "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="], + + "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], + + "make-fetch-happen": ["make-fetch-happen@9.1.0", "", { "dependencies": { "agentkeepalive": "^4.1.3", "cacache": "^15.2.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^6.0.0", "minipass": "^3.1.3", "minipass-collect": "^1.0.2", "minipass-fetch": "^1.3.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.2", "promise-retry": "^2.0.1", "socks-proxy-agent": "^6.0.0", "ssri": "^8.0.0" } }, "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "media-typer": ["media-typer@0.3.0", "", {}, "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ=="], + + "merge-descriptors": ["merge-descriptors@1.0.3", "", {}, "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ=="], + + "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], + + "mime": ["mime@1.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], + + "minimatch": ["minimatch@3.1.2", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw=="], + + "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], + + "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + + "minipass-collect": ["minipass-collect@1.0.2", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA=="], + + "minipass-fetch": ["minipass-fetch@1.4.1", "", { "dependencies": { "minipass": "^3.1.0", "minipass-sized": "^1.0.3", "minizlib": "^2.0.0" }, "optionalDependencies": { "encoding": "^0.1.12" } }, "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw=="], + + "minipass-flush": ["minipass-flush@1.0.5", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw=="], + + "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], + + "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + + "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], + + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + + "ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + + "negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="], + + "node-abi": ["node-abi@3.74.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w=="], + + "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + + "node-gyp": ["node-gyp@8.4.1", "", { "dependencies": { "env-paths": "^2.2.0", "glob": "^7.1.4", "graceful-fs": "^4.2.6", "make-fetch-happen": "^9.1.0", "nopt": "^5.0.0", "npmlog": "^6.0.0", "rimraf": "^3.0.2", "semver": "^7.3.5", "tar": "^6.1.2", "which": "^2.0.2" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w=="], + + "nopt": ["nopt@5.0.0", "", { "dependencies": { "abbrev": "1" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ=="], + + "npmlog": ["npmlog@6.0.2", "", { "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + + "on-headers": ["on-headers@1.0.2", "", {}, "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="], + + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + + "p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], + + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + + "path-to-regexp": ["path-to-regexp@0.1.12", "", {}, "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="], + + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + + "promise-inflight": ["promise-inflight@1.0.1", "", {}, "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="], + + "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + + "pump": ["pump@3.0.2", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw=="], + + "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], + + "random-bytes": ["random-bytes@1.0.0", "", {}, "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@2.5.2", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.4.24", "unpipe": "1.0.0" } }, "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA=="], + + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + + "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], + + "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + + "semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="], + + "send": ["send@0.19.0", "", { "dependencies": { "debug": "2.6.9", "depd": "2.0.0", "destroy": "1.2.0", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "etag": "~1.8.1", "fresh": "0.5.2", "http-errors": "2.0.0", "mime": "1.6.0", "ms": "2.1.3", "on-finished": "2.4.1", "range-parser": "~1.2.1", "statuses": "2.0.1" } }, "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw=="], + + "serve-static": ["serve-static@1.16.2", "", { "dependencies": { "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "parseurl": "~1.3.3", "send": "0.19.0" } }, "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw=="], + + "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], + + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], + + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], + + "socks": ["socks@2.8.4", "", { "dependencies": { "ip-address": "^9.0.5", "smart-buffer": "^4.2.0" } }, "sha512-D3YaD0aRxR3mEcqnidIs7ReYJFVzWdd6fXJYUM8ixcQcJRGTka/b3saV0KflYhyVJXKhb947GndU35SxYNResQ=="], + + "socks-proxy-agent": ["socks-proxy-agent@6.2.1", "", { "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" } }, "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ=="], + + "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], + + "sqlite3": ["sqlite3@5.1.7", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^7.0.0", "prebuild-install": "^7.1.1", "tar": "^6.1.11" }, "optionalDependencies": { "node-gyp": "8.x" } }, "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog=="], + + "ssri": ["ssri@8.0.1", "", { "dependencies": { "minipass": "^3.1.1" } }, "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ=="], + + "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], + + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], + + "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + + "supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + + "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], + + "tar-fs": ["tar-fs@2.1.2", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-EsaAXwxmx8UB7FRKqeozqEPop69DXcmYwTQwXvyAPF352HJsPdkVhvTaDPYqfNgruveJIJy3TA2l+2zj8LJIJA=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + + "type-is": ["type-is@1.6.18", "", { "dependencies": { "media-typer": "0.3.0", "mime-types": "~2.1.24" } }, "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g=="], + + "uid-safe": ["uid-safe@2.1.5", "", { "dependencies": { "random-bytes": "~1.0.0" } }, "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA=="], + + "unique-filename": ["unique-filename@1.1.1", "", { "dependencies": { "unique-slug": "^2.0.0" } }, "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ=="], + + "unique-slug": ["unique-slug@2.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w=="], + + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], + + "validator": ["validator@13.12.0", "", {}, "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg=="], + + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "wide-align": ["wide-align@1.1.5", "", { "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg=="], + + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], + + "yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="], + + "agent-base/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + + "express-session/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "express-session/cookie-signature": ["cookie-signature@1.0.7", "", {}, "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA=="], + + "filelist/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="], + + "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "http-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "https-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "humanize-ms/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "make-fetch-happen/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], + + "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "socks-proxy-agent/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + + "ssri/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "agent-base/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="], + + "http-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "https-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "socks-proxy-agent/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + } +} diff --git a/messages/package.json b/messages/package.json new file mode 100644 index 0000000..3e58dd0 --- /dev/null +++ b/messages/package.json @@ -0,0 +1,11 @@ +{ + "name": "KeyNotes.App", + "type": "module", + "dependencies": { + "connect-sqlite3": "^0.9.15", + "ejs": "^3.1.10", + "express": "^4.21.2", + "express-session": "^1.18.1", + "express-validator": "^7.2.1" + } +} diff --git a/messages/public/css/style.css b/messages/public/css/style.css new file mode 100644 index 0000000..4e11935 --- /dev/null +++ b/messages/public/css/style.css @@ -0,0 +1,428 @@ +:root { + --font-size: 18px; + --desktop-heading-font-size: 5em; + --desktop-line-height: 1; + --mobile-heading-font-size: 4em; + --mobile-line-height: 0.8; + --border-radius-sm: 0.5em; + --border-radius-md: 0.7em; + --border-radius-lg: 1em; + + /* colors */ + + --100-blue: hsl(219, 64%, 96%, 100%); + --200-blue: hsl(222, 81%, 94%, 100%); + --300-blue: hsl(221, 86%, 92%, 100%); + --400-blue: hsl(222, 92%, 90%, 100%); + --500-blue: hsl(222, 97%, 88%, 100%); + --600-blue: hsl(222, 97%, 85%, 100%); + --700-blue: hsl(222, 100%, 84%, 100%); + --800-blue: hsl(222, 100%, 34%, 100%); +} +/* 1. Use a more-intuitive box-sizing model */ +*, +*::before, +*::after { + box-sizing: border-box; +} + +/* 2. Remove default margin */ +* { + margin: 0; +} + +body { + /* 3. Add accessible line-height */ + line-height: 1.5; + /* 4. Improve text rendering */ + -webkit-font-smoothing: antialiased; + font-family: "Lato, serif"; + background: var(--100-blue); + min-height: 100vh; + display: flex; + flex-direction: column; +} + +/* 5. Improve media defaults */ +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; +} + +/* 6. Inherit fonts for form controls */ +input, +button, +textarea, +select { + font: inherit; +} + +/* 7. Avoid text overflows */ +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +/* 8. Improve line wrapping */ +p { + text-wrap: pretty; +} +h1, +h2, +h3, +h4, +h5, +h6 { + text-wrap: balance; +} + +a { + color: var(--800-blue); + text-decoration: none; +} + +/* + 9. Create a root stacking context +*/ +#root, +#__next { + isolation: isolate; +} + +nav { + font-size: 1.5rem; + padding: 1em 0; +} + +nav > ul { + list-style: none; + text-align: center; + margin: 0 auto; +} + +ul { + padding: 0; + margin: auto; + justify-content: space-around; + max-width: 80vw; +} + +ul > li { + display: inline-block; + text-align: center; +} + +li a { + color: black; +} + +form { + width: 100%; + max-width: 50vw; +} + +input { + padding: 0.75em; +} + +footer { + margin-top: auto; + padding: 2em; +} + +/*.full-page {*/ +/* height: 90vh;*/ +/*}*/ + +.divider { + height: 0.25em; + background: var(--300-blue); + width: 80vw; + margin: 0 auto 1em; +} + +.flex { + display: flex; +} + +.col { + flex-direction: column; +} + +.container.flex.hero { + align-items: center; + justify-content: center; + min-height: 60vh; +} + +.container.hero { + text-align: center; +} + +.container.hero * { + margin: 0.5em 0; +} + +.container.hero p { + font-size: 1.2rem; + padding: 1em; +} + +.center-horizontal { + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 1em; + margin: 0 auto; +} + +.hero__heading { + margin: 0; + font-size: var(--mobile-heading-font-size); + font-family: "Rokkitt", serif; + line-height: var(--desktop-line-height); +} + +.btn { + display: inline-block; + width: 100%; + padding: 1em; + text-decoration: none; + border-radius: 0.7em; + font-size: 1em; +} + +.btn__link { + text-align: center; + background: #f0f0f0; + line-height: 1.5em; + max-width: 200px; +} + +.btn__link:hover { + background: #bfbba9; +} + +.btn__form { + background: #f0f0f0; +} + +.btn__form:hover { + background: #bfbba9; +} + +.hero > .btn__link { + margin-top: 2em; +} + +#main { + padding: 0 50px; +} + +#main > .container.flex { + justify-content: center; + align-items: center; + flex-wrap: wrap; +} + +.feed-heading { + margin-left: 1em; + text-transform: capitalize; + text-align: center; +} + +.feed-heading span { + text-transform: initial; +} + +.author__meta { + gap: 0.5em; +} + +.grid { + display: grid; + /*grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));*/ + gap: 1em; + padding: 2em; + grid-auto-flow: dense; + margin: 0 auto; +} + +.grid-item { + display: flex; + flex-direction: column; + justify-content: space-between; + gap: 2em; + position: relative; +} + +.grid-item.card { + border: 1px solid black; + border-radius: 0.5em; + text-align: center; + background: var(--500-blue); +} + +.grid-item > p { + padding: 5em; +} + +.grid__card__metadata { + position: absolute; + bottom: 0; + display: flex; + font-size: 0.8em; + justify-content: space-between; + width: 100%; + padding: 0 0.9em 0.7em; +} + +.likes { + display: flex; + align-self: center; + width: auto; + align-items: center; + justify-content: center; + gap: 0.5em; +} + +.card__likes { + padding: 0; + font-weight: 400; +} + +.timestamp { + font-weight: 300; +} + +.card .card__message { + margin-bottom: 1em; +} + +.form-item { + flex-direction: column; + width: 100%; + margin: 0.5em 0; +} + +.form-item.flex label { + text-align: left; + padding-left: 0.2em; +} + +.form-item-row { + flex-direction: column; + width: 100%; + margin: 0.5em 0; + gap: 0.5em; +} + +.hover__underline { + /*display: inline-block;*/ + position: relative; +} + +.red { + color: red; +} + +.hover__underline::after { + content: ""; + position: absolute; + width: 100%; + transform: scaleX(0); + height: 2px; + bottom: 0; + left: 0; + background-color: var(--700-blue); + transform-origin: bottom left; + transition: transform 0.25s ease-out; +} + +.hover__underline:hover::after { + transform: scaleX(1); + transform-origin: bottom left; +} + +.hidden { + display: none; +} + +.social-icons { + gap: 0.5em; + justify-content: baseline; +} + +.social-icons a { + font-size: 1.2em; + color: var(--800-blue); +} + +@media only screen and (min-width: 750px) { + /*ul {*/ + /* max-width: 40vw;*/ + /*}*/ + .hero__heading { + margin: 0; + font-size: var(--desktop-heading-font-size); + font-family: "Rokkitt", serif; + line-height: var(--desktop-line-height); + } + .container.hero p { + font-size: 1.5rem; + padding: 0.5em; + } + .container.flex * { + flex: 1; + } + #main { + padding: 0 100px; + } + + #main > .container.flex { + gap: 10em; + } + + .grid { + width: 100%; + max-width: 750px; + } + .grid__span_two { + grid-column-end: span 2; + } + .form-item-row { + flex-direction: row; + width: 100%; + margin: 0.5em 0; + gap: 0.5em; + } + + .nav__profile { + position: relative; + } + + .nav__hover { + position: absolute; + background: var(--100-blue); + border: 2px solid black; + width: 100%; + /*max-width: 150px;*/ + border-radius: var(--border-radius-sm); + z-index: 1; + } + + /*.form-item > .btn {*/ + /* max-width: 200px;*/ + /*}*/ +} diff --git a/messages/public/images/favicon.png b/messages/public/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..e98cbc1301b7cdd2951d28cf99b0eca7570b4046 GIT binary patch literal 1240 zcmV;}1Sk86P)<h;3K|Lk000e1NJLTq001BW001Be1^@s6b9#F8000D^Nkl<ZSV!$w zeN0<b6#u=J;$TV{jPbFy(6W{?O8FAl*FXEi1jLMpQ|H`<F>JE97@cM&IB_%nY21c{ zL^m}tK{It*9GROgPP6GKu)_GV;L66=fPp~i`bA&c=Y4PMT|t+u0$WUWlcx8)^X|F7 z^SkGqcP@i~q19@m5xVFVFrp%GNF%BUh76A5K>h+>4SLQ2UShMy$N6e)<2ODW8X9u& zMyOS_F1ti5Vh)piqIiW=Dn;ayB@m0n!e%0oDCkZ73kZH#r=~DAHYz+K4jH*=`}*$g z?wbMtO?p-#*`SUHVx=k>Wjo3tS19J}bs%D%#V(TxHPwe;nVbaY;|_GSH}4hzX!SW= z#Pbw>UR7O#goNe)+1r0xNNC5jn-x?&W;6D^TR~XBcvqWo8KczcVjmHO4)bTtzi{NU z&moh^<`n>M%;WLEVzrSl0N&eMfvY`cI6ba-W`$lalQ5D2|Bf*+F;HtX0{wFhJl<w_ zSQzs1^RagAI@laexTf8xI`|Qq8Vq1LR?ej5<S2X|^v!m8J|aKRW<fzAc9idg-RZ)| zhYq08Q11t@jsS4}1Hc<^?Lm@K1&70d6LqJkt?bMUq^4-7Tzm9eI@fB{$jQp|M>@VH zx<5@FnEdNj^jGfN4|PiFy#c6v{{yJi8rbZ1eD%#Sy2+~C$D!A)r1Ga<d`ai&D^syL zKQ{o}cf$I1qXAPB<JeIAG@jk`!u<tcCk&2_456#783j)k;l-_`_XePB=PptU5{2mP z>!&e_=%^?}MoOud=Bw9eHIqssurxZF>UzmKFIhi&V+3bT9uE#cM&24kL`ESzJRDn| z-$;$tH#VWY(?sR%FKwmS($@J042_qlZu4``;L*j4VZL$&`zzmh2p~8HcD?>45|fm0 zyWMDMZKt#<<TAv^#ZuYOWTY_=7rP9J3OUs^x3+;LC3u{q;V(a)3J$=0r6HdMR?7rh z8!!IP0Psh^SHfv>1kIOQDXoceITGUIW@Lq5h>wj$Qlf(Dj4iEjvu=!&KH%4%eh7|% zhiUl087M1%RVc+QDW&~`*Xg-NlRhz$lwAMlxz<ZM%Ui9bG~zKVC8gRw`UVE!@p>^v zO7ZFMj|CS4_bm-|xw&$$+t*KF-<~WqfQI^u9Ndl==57G?RP2Q+c?FzKC(fKbNAc;> zQlU;(QTfElJIzY6N;#ufIDPhKxJU<NA-zDug>y5GprO9Lg*2tvcLlJJL0D5=W%>R5 z`6s$NS}r{h05@&cE$j2LYIzzNy*^*YGM+mf$8B;Kgn>fR5p64lhxM&qm7z8W0Fu(u z;=)1}4bKk3&8076xl9h9kAu_YqUunI1QJOImHCg2|Ar?C2|;M6gz6mR{Q2&BWav7M zAFV}U(Nm-)+t!q=Rps+nP5}V<iM9G{{$dgn2x307V0fSpW>Y(gOE&(tp}1tzn#>eh zScD3aqRV=X5itk-w<G@9d;yrUTG3*-0EgXXvQLcW3=R%D10AKeV@-PIb}=J*oA9C} zKtyu|z_KhpIeY{imyI@?)l%p3x_9?>c8<(G>c0VFp08n7nLcX(0000<MNUMnLSTYY CEmEBT literal 0 HcmV?d00001 diff --git a/messages/public/js/script.js b/messages/public/js/script.js new file mode 100644 index 0000000..12f5e24 --- /dev/null +++ b/messages/public/js/script.js @@ -0,0 +1,109 @@ +function usernameValidation(username) { + const errors = []; + if (username.length < 5) { + errors.push("*username must be at least 8 characters"); + } + + if (username.includes(" ")) { + errors.push("*username must not contain any spaces"); + } + + return errors; +} + +function checkStringForNumber(str) { + for (i = 0; i < str.length; i++) { + if (!isNaN(str[i])) return true; + } + return false; +} + +function checkStringForChar(str) { + return /[!#%^,\.]/.test(str); +} + +function passwordValidation(password) { + const errors = []; + + if (password.length < 8) { + errors.push("*password must be at least 8 characters"); + } + + if (!checkStringForNumber(password)) { + errors.push("*password must include a number."); + } + if (!checkStringForChar(password)) { + errors.push("*password must include a special character. !#%^,."); + } + + return errors; +} + +function formValidator() { + const username = document.querySelector('input[name="username"]'); + const password = document.querySelector('input[name="password"]'); + const passwordConfirmation = document.querySelector( + 'input[name="password-confirmation"]', + ); + + if (username && password && passwordConfirmation) { + username.addEventListener("keyup", () => { + let errors = usernameValidation(username.value); + let errorDiv = username.nextElementSibling; + errorDiv.textContent = ""; + + if (errors.length > 0) { + errors.forEach((err) => { + const p = document.createElement("p"); + p.textContent = err; + errorDiv.appendChild(p); + }); + } else { + errorDiv.textContent = ""; + } + }); + + password.addEventListener("keyup", (e) => { + let errorDiv = password.nextElementSibling; + errorDiv.textContent = ""; + let errors = passwordValidation(e.target.value); + + if (errors.length > 0) { + errors.forEach((err) => { + const p = document.createElement("p"); + p.textContent = err; + errorDiv.appendChild(p); + }); + } else { + errorDiv.textContent = ""; + } + }); + + passwordConfirmation.addEventListener("keyup", (e) => { + let error = "*passwords do not match!"; + let errorDiv = passwordConfirmation.nextSibling; + + if (password.value !== e.target.value) { + errorDiv.textContent = error; + } else { + errorDiv.textContent = ""; + } + }); + } +} + +function navProfileHover() { + const profile = document.querySelector(".nav__profile"); + if (profile) { + const profileHover = document.querySelector(".nav__hover"); + profile.addEventListener("mouseleave", () => { + profileHover.classList.add("hidden"); + }); + profile.addEventListener("mouseenter", () => { + profileHover.classList.remove("hidden"); + }); + } +} + +navProfileHover(); +formValidator(); diff --git a/messages/src/app.js b/messages/src/app.js new file mode 100644 index 0000000..31a5a15 --- /dev/null +++ b/messages/src/app.js @@ -0,0 +1,53 @@ +const fs = require("node:fs"); +const { logging } = require("./middlewares/logging"); +const path = require("node:path"); +const express = require("express"); +const passport = require("passport"); +const session = require("express-session"); +const SQLiteStore = require("connect-sqlite3")(session); + +const { indexRouter } = require("./routes/indexRouter"); +const { authRouter } = require("./routes/authRouter"); + +const app = express(); + +const assetsPath = path.join(path.dirname(__dirname), "public"); +app.use(express.static(assetsPath)); + +app.set("views", path.join(__dirname, "views")); +app.set("view engine", "ejs"); + +app.use( + session({ + store: new SQLiteStore({ dir: "./src/db/", db: "keynotes.db" }), + secret: "keynotes.app", + cookie: { maxAge: 7 * 24 * 60 * 60 * 1000 }, + saveUninitialized: false, + resave: false, + }), +); + +app.use(passport.session()); +app.use(express.urlencoded({ extended: false })); +app.use(logging); +app.use((req, res, next) => { + res.locals.currentUser = req.user; + next(); +}); + +app.use("/", indexRouter); +app.use("/auth", authRouter); + +const server = app.listen(5173, () => console.log(`App running on port: 5173`)); + +const gracefulShutdownHandler = (signal) => { + console.log(`\n${signal} received.`); + + server.close(() => { + console.log("Shutting down the server process..."); + process.exit(); + }); +}; + +process.on("SIGINT", gracefulShutdownHandler); +process.on("SIGTERM", gracefulShutdownHandler); diff --git a/messages/src/config.js b/messages/src/config.js new file mode 100644 index 0000000..e69de29 diff --git a/messages/src/controllers/authController.js b/messages/src/controllers/authController.js new file mode 100644 index 0000000..19d7a62 --- /dev/null +++ b/messages/src/controllers/authController.js @@ -0,0 +1,46 @@ +const bcryptjs = require("bcryptjs"); +const db = require("../models/query"); +const { validationResult } = require("express-validator"); + +function loginGet(req, res, next) { + res.render("login", { + pageTitle: "InspiredCliches | Login", + errors: req.session.messages, + }); +} + +function logOut(req, res, next) { + req.logout((err) => { + if (err) return next(err); + }); + res.redirect("/"); +} + +function signUpGet(req, res, next) { + res.render("register", { + pageTitle: "InspiredCliches | Register", + errors: null, + }); +} + +async function signUpPost(req, res, next) { + const result = validationResult(req); + if (result.isEmpty()) { + const { username, password } = req.body; + const hashedPassword = await bcryptjs.hash(password, 10); + db.insertUser(username, hashedPassword); + res.redirect("/auth/login"); + } else { + res.status(400).render("register", { + errors: result.array(), + pageTitle: "InspiredCliches | Register", + }); + } +} + +module.exports = { + loginGet, + logOut, + signUpPost, + signUpGet, +}; diff --git a/messages/src/controllers/indexController.js b/messages/src/controllers/indexController.js new file mode 100644 index 0000000..2314b6a --- /dev/null +++ b/messages/src/controllers/indexController.js @@ -0,0 +1,90 @@ +const db = require("../models/query"); + +function indexGet(req, res, next) { + if (res.locals.currentUser) { + const keynotes = db.getEveryNote(); + const userLikedPosts = db.getLikesByUser(res.locals.currentUser.user_id); + + res.render("feed", { + pageTitle: "InspiredCliches | Feed", + keynotes: keynotes, + userLikedPosts: userLikedPosts, + }); + } + res.render("home", { pageTitle: "InspiredCliches" }); +} + +function addLike(req, res, next) { + const { noteId } = req.query; + + const noteExits = noteId ? db.getNoteById(noteId) : null; + + if (res.locals.currentUser && noteExits) { + const userId = res.locals.currentUser.user_id; + if (db.checkIfNotedLiked(noteId, userId)) { + db.deleteLike(noteId, userId); + } else { + db.insertLike(Number(noteId), userId); + } + } + res.redirect("/"); +} + +function addNotePost(req, res, next) { + const { message, media } = req.body; + + if (res.locals.currentUser) { + const userId = res.locals.currentUser.user_id; + + db.putNewNote({ message: message, media: null, userId: userId }); + } + + res.redirect("/"); +} + +function deleteNote(req, res) { + const { messageId, userId } = req.query; + + if (res.locals.currentUser) { + const message = db.getNoteById(messageId); + + if (message.user_id === Number(userId)) { + db.deleteNote(Number(messageId)); + } + res.redirect("/"); + } +} + +function addNoteGet(req, res, next) { + res.render("note-form", { pageTitle: "InspiredCliches | New Note" }); +} + +function getProfile(req, res, next) { + const { userId } = req.query; + + const userExists = userId ? db.getUserById(userId) : null; + + if (res.locals.currentUser) { + if (userExists) { + const sumUserPosts = db.getSumNotesByUserId(userId); + const allPosts = db.getAllNotesByUserId(userId); + res.render("view-profile", { + pageTitle: "InspiredCliches | Edit Profile", + user: userExists, + totalPosts: sumUserPosts, + allPosts: allPosts, + }); + } else { + res.redirect("/"); + } + } +} + +module.exports = { + indexGet, + addNoteGet, + addNotePost, + deleteNote, + addLike, + getProfile, +}; diff --git a/messages/src/middlewares/auth.js b/messages/src/middlewares/auth.js new file mode 100644 index 0000000..d62e7fd --- /dev/null +++ b/messages/src/middlewares/auth.js @@ -0,0 +1,50 @@ +const bcryptjs = require("bcryptjs"); +const db = require("../models/db"); +const LocalStrategy = require("passport-local"); +const passport = require("passport"); + +passport.use( + new LocalStrategy( + { passReqToCallback: true }, + async (req, username, password, done) => { + try { + req.session.messages = []; + const query = db.query(`SELECT * FROM users WHERE username = $1`); + const user = query.get({ $1: username }); + + if (!user) { + return done(null, false, { + message: "Incorrect username or password.", + }); + } + + const match = await bcryptjs.compare(password, user.password); + + if (!match) { + return done(null, false, { + message: "Incorrect username or password.", + }); + } + return done(null, user); + } catch (err) { + return done(err); + } + }, + ), +); + +passport.serializeUser((user, done) => { + done(null, user.user_id); +}); + +passport.deserializeUser(async (id, done) => { + try { + const query = db.query("SELECT * FROM users WHERE user_id = $userId"); + const results = query.get({ $userId: id }); + done(null, results); + } catch (err) { + done(err); + } +}); + +module.exports = passport; diff --git a/messages/src/middlewares/logging.js b/messages/src/middlewares/logging.js new file mode 100644 index 0000000..ab8863d --- /dev/null +++ b/messages/src/middlewares/logging.js @@ -0,0 +1,8 @@ +function logging(req, res, next) { + req.time = new Date(Date.now()).toISOString(); + const clientIp = req.header("cf-connecting-ip") || req.socket.remoteAddress; + console.log(req.time, req.method, req.hostname, req.path, clientIp); + next(); +} + +module.exports = { logging }; diff --git a/messages/src/models/db.js b/messages/src/models/db.js new file mode 100644 index 0000000..0394ecd --- /dev/null +++ b/messages/src/models/db.js @@ -0,0 +1,35 @@ +const { Database } = require("bun:sqlite"); +const fs = require("node:fs"); +const path = require("node:path"); +const { mkdir } = require("node:fs/promises"); +const { stat } = require("node:fs"); + +const dbDirPath = path.join(path.dirname(__dirname), "/db"); +const dbPath = path.join(dbDirPath, "/keynotes.db"); + +async function makeDirectory(path) { + const dirCreation = await mkdir(path); + return dirCreation; +} + +// Make sure DB path exists +stat(dbDirPath, (err, stats) => { + if (err !== null) { + makeDirectory(dbDirPath); + } +}); + +const db = new Database(dbPath); +// Enable WAL Mode +db.exec("PRAGMA journal_mode = WAL;"); + +const initSQL = path.join(__dirname, "./init.sql"); +fs.readFile(initSQL, "utf8", (err, data) => { + if (err) { + console.error(err); + return; + } + db.exec(data); +}); + +module.exports = db; diff --git a/messages/src/models/init.sql b/messages/src/models/init.sql new file mode 100644 index 0000000..5e2c3a9 --- /dev/null +++ b/messages/src/models/init.sql @@ -0,0 +1,73 @@ +CREATE TABLE IF NOT EXISTS users ( + user_id INTEGER PRIMARY KEY ASC, + username VARCHAR(50) UNIQUE NOT NULL, + password TEXT NOT NULL +); + +CREATE TABLE IF NOT EXISTS user_profile_info ( + user_info_id INTEGER PRIMARY KEY ASC, + email VARCHAR(50), + user_id INTEGER REFERENCES users(user_id) +); + +CREATE TABLE IF NOT EXISTS messages ( + message_id INTEGER PRIMARY KEY ASC, + message TEXT, + media VARCHAR(100), + date NUMBER, + user_id INTEGER REFERENCES users(user_id) +); + +CREATE TABLE IF NOT EXISTS likes ( + likes_id INTEGER PRIMARY KEY ASC, + message_id INTEGER REFERENCES messages(message_id), + user_id INTEGER REFERENCES users(user_id), + liked_at NUMBER +); + + + +INSERT OR IGNORE INTO users (username, password) +VALUES ("jim", "$2a$10$Bmjre5WSpSSAi.nWBfLZFOlhQhbIAoY/MM7ikJz3Ho9tqeXCExaB6"), + ("demo", "$2a$10$Vo.XmsVQVx9gGojRIdewpOap5SnhrgZ21/Im5IxhH3PC4FycM5uwC"); + +INSERT OR IGNORE INTO messages (message_id, message, media, date, user_id) +VALUES + (1, "When life gives you lemons, make lemonade.", NULL, strftime('%s', 'now')*1000, 1), + (2, "Rome wasn’t built in a day – take your time.", NULL, strftime('%s', 'now')*1000, 1), + (3, "The grass is always greener on the other side.", NULL, strftime('%s', 'now')*1000, 1), + (4, "If it ain’t broke, don’t fix it.", NULL, strftime('%s', 'now')*1000, 1), + (5, "You can’t have your cake and eat it too.", NULL, strftime('%s', 'now')*1000, 1), + (6, "Better late than never.", NULL, strftime('%s', 'now')*1000, 1), + (7, "What doesn’t kill you makes you stronger.", NULL, strftime('%s', 'now')*1000, 1), + (8, "Every cloud has a silver lining.", NULL, strftime('%s', 'now')*1000, 1), + (9, "Don’t bite the hand that feeds you.", NULL, strftime('%s', 'now')*1000, 1), + (10, "Absence makes the heart grow fonder.", NULL, strftime('%s', 'now')*1000, 1), + (11, "Always trust your gut feeling – it’s usually right.", NULL, strftime('%s', 'now')*1000, 2), + (12, "Money can’t buy happiness, but it sure helps.", NULL, strftime('%s', 'now')*1000, 2), + (13, "Everything happens for a reason.", NULL, strftime('%s', 'now')*1000, 2), + (14, "Don’t go to bed angry – resolve conflicts before sleeping.", NULL, strftime('%s', 'now')*1000, 2), + (15, "The early bird catches the worm.", NULL, strftime('%s', 'now')*1000, 2), + (16, "You only live once – make the most of it.", NULL, strftime('%s', 'now')*1000, 2), + (17, "Hard work pays off in the end.", NULL, strftime('%s', 'now')*1000, 2), + (18, "Family is everything – cherish them.", NULL, strftime('%s', 'now')*1000, 2), + (19, "Don’t put off until tomorrow what you can do today.", NULL, strftime('%s', 'now')*1000, 2), + (20, "A smile costs nothing but gives so much.", NULL, strftime('%s', 'now')*1000, 2), + (21, "You can’t please everyone – focus on yourself.", NULL, strftime('%s', 'now')*1000, 2), + (22, "Health is wealth – take care of your body.", NULL, strftime('%s', 'now')*1000, 2), + (23, "Patience is a virtue – good things take time.", NULL, strftime('%s', 'now')*1000, 2), + (24, "Don’t judge a book by its cover.", NULL, strftime('%s', 'now')*1000, 2), + (25, "Actions speak louder than words.", NULL, strftime('%s', 'now')*1000, 2), + (26, "Learn from your mistakes – they make you wiser.", NULL, strftime('%s', 'now')*1000, 2), + (27, "Kindness costs nothing but means everything.", NULL, strftime('%s', 'now')*1000, 2), + (28, "Life is short – eat the cake.", NULL, strftime('%s', 'now')*1000, 2), + (29, "Comparison is the thief of joy – focus on your journey.", NULL, strftime('%s', 'now')*1000, 2), + (30, "The best things in life are free.", NULL, strftime('%s', 'now')*1000, 2); + +-- ALTER TABLE likes +-- ADD COLUMN user_id INTEGER REFERENCES users(user_id); + +-- ALTER TABLE likes +-- ADD COLUMN liked_at NUMBER; + +-- ALTER TABLE likes DROP COLUMN sum_likes; diff --git a/messages/src/models/query.js b/messages/src/models/query.js new file mode 100644 index 0000000..6ec5f50 --- /dev/null +++ b/messages/src/models/query.js @@ -0,0 +1,138 @@ +const db = require("./db"); + +function getEveryNote() { + try { + const query = db.query( + `SELECT messages.message_id, message, media, date, users.username AS user, messages.user_id, COUNT(likes.likes_id) AS likes_count + FROM messages LEFT JOIN users ON messages.user_id = users.user_id + LEFT JOIN likes ON messages.message_id = likes.message_id + GROUP BY messages.message_id, message, media, date, users.username + ORDER BY date DESC + `, + ); + + const data = query.all(); + return data; + } catch (err) { + return err; + } +} + +function putNewNote(note) { + const { message, media, userId } = note; + const date = Date.now(); + const query = db.query( + `INSERT INTO messages (message, media, user_id, date) VALUES ($1, $2, $3, $4)`, + ); + query.run({ $1: message, $2: media, $3: userId, $4: date }); +} + +function deleteNote(messageId) { + try { + const query = db.query(`DELETE FROM likes WHERE message_id = $1`); + query.run({ $1: messageId }); + const queryTwo = db.query(`DELETE FROM messages WHERE message_id = $1`); + queryTwo.run({ $1: messageId }); + } catch (e) { + console.log(e); + } +} + +function getLikesByUser(userId) { + const query = db.query(`SELECT message_id FROM likes WHERE user_id = $1`); + + return query.all({ $1: userId }); +} + +function getTotalLikesByMessageId(messageId) { + const query = db.query(`SELECT COUNT(1) FROM likes WHERE message_id = $1`); + return query.get({ $1: messageId }); +} + +function getNoteById(noteId) { + const query = db.query( + `SELECT message, user_id from messages WHERE message_id = $1`, + ); + return query.get({ $1: noteId }); +} + +function checkIfNotedLiked(messageId, userId) { + const query = db.query( + `SELECT * FROM likes WHERE message_id = $1 and user_id = $2`, + ); + + const res = query.get({ $1: messageId, $2: userId }); + return res; +} + +function insertLike(messageId, userId) { + try { + const query = db.query( + `INSERT INTO likes (message_id, user_id, liked_at) VALUES ($1, $2, $3)`, + ); + query.run({ $1: messageId, $2: userId, $3: Date.now() }); + } catch (err) { + console.log(err); + return err; + } +} + +function deleteLike(messageId, userId) { + try { + const query = db.query( + `DELETE FROM likes WHERE message_id = $1 and user_id = $2`, + ); + query.run({ $1: messageId, $2: userId }); + } catch (err) { + console.log(err); + return err; + } +} + +function insertUser(username, password) { + try { + const query = db.query( + "INSERT INTO users (username, password) VALUES ($username, $password)", + ); + query.run({ $username: username, $password: password }); + } catch (err) { + return err; + } +} +function getUserById(userId) { + const query = db.query( + `SELECT username, user_id FROM users WHERE user_id = $1`, + ); + const res = query.get({ $1: userId }); + + return res; +} + +function getSumNotesByUserId(userId) { + const query = db.query(`SELECT COUNT(1) FROM messages WHERE user_id = $1`); + const res = query.get({ $1: userId }); + + return res["COUNT(1)"]; +} +function getAllNotesByUserId(userId) { + const query = db.query(`SELECT * FROM messages WHERE user_id = $1`); + const res = query.all({ $1: userId }); + + return res; +} + +module.exports = { + getEveryNote, + getNoteById, + putNewNote, + insertLike, + deleteLike, + deleteNote, + checkIfNotedLiked, + insertUser, + getLikesByUser, + getTotalLikesByMessageId, + getUserById, + getSumNotesByUserId, + getAllNotesByUserId, +}; diff --git a/messages/src/routes/authRouter.js b/messages/src/routes/authRouter.js new file mode 100644 index 0000000..52912a7 --- /dev/null +++ b/messages/src/routes/authRouter.js @@ -0,0 +1,32 @@ +const { Router } = require("express"); +const authController = require("../controllers/authController"); +const passport = require("../middlewares/auth"); +const { body } = require("express-validator"); + +const authRouter = Router(); + +//authRouter.get("/", (req, res) => res.redirect("/auth/login")); +authRouter.get("/login", authController.loginGet); +authRouter.post( + "/login", + passport.authenticate("local", { + successRedirect: "/", + failureRedirect: "/auth/login", + failureMessage: true, + }), +); +authRouter.get("/logout", authController.logOut); +authRouter.get("/register", authController.signUpGet); +authRouter.post( + "/register", + body("password").isLength({ min: 5 }).withMessage("Password is too short"), + body("password-confirmation").custom((value, { req }) => { + if (value !== req.body.password) { + throw new Error("Passwords do not match!"); + } + return true; + }), + authController.signUpPost, +); + +module.exports = { authRouter }; diff --git a/messages/src/routes/indexRouter.js b/messages/src/routes/indexRouter.js new file mode 100644 index 0000000..badc508 --- /dev/null +++ b/messages/src/routes/indexRouter.js @@ -0,0 +1,13 @@ +const { Router } = require("express"); +const indexController = require("../controllers/indexController"); + +const indexRouter = Router(); + +indexRouter.get("/", indexController.indexGet); +indexRouter.get("/like", indexController.addLike); +indexRouter.get("/new", indexController.addNoteGet); +indexRouter.post("/new", indexController.addNotePost); +indexRouter.get("/profile", indexController.getProfile); +indexRouter.get("/delete", indexController.deleteNote); + +module.exports = { indexRouter }; diff --git a/messages/src/views/content.ejs b/messages/src/views/content.ejs new file mode 100644 index 0000000..e69de29 diff --git a/messages/src/views/feed.ejs b/messages/src/views/feed.ejs new file mode 100644 index 0000000..2188d2a --- /dev/null +++ b/messages/src/views/feed.ejs @@ -0,0 +1,31 @@ +<%- include('header') %> +<div class="grid"> + <% keynotes.forEach((note) => { %> + <% const likedPost = userLikedPosts.find((post) => post.message_id == note.message_id) %> + <% let extraMessageClass = "" %> + <% if (note.message.length > 250) { %> + <% extraMessageClass = " grid__span_two"; %> + <% }; %> + <div class="grid-item card<%= extraMessageClass %>"> + <p class="card__message"><%= note.message %></p> + <div class="container grid__card__metadata"> + <div class="flex author__meta"> + <p class="card__author"><a href="/profile?userId=<%= note.user_id %>"><%= note.user %></a></p> + <p class="card__likes">•</p> + <p class="card__likes timestamp"><%= new Date(note.date).toLocaleString() %></p> + </div> + <% if (currentUser.user_id === note.user_id) { %> + <a href="/delete?userId=<%= currentUser.user_id %>&messageId=<%= note.message_id %>">delete post</a> + <% }; %> + <div class="likes"> + + <a href="/like?noteId=<%= note.message_id %>"> + <% if (likedPost) { %><i class="fa-solid fa-heart red"></i><% } else { %><i class="fa-regular fa-heart red"></i><% }; %> + </a> + <p class="card__likes"><%= note.likes_count %></p> + </div> + </div> + </div> + <% }); %> +</div> +<%- include('footer') %> diff --git a/messages/src/views/footer.ejs b/messages/src/views/footer.ejs new file mode 100644 index 0000000..fc5b341 --- /dev/null +++ b/messages/src/views/footer.ejs @@ -0,0 +1,12 @@ + <footer class="container"> + <h2 class="foooter-heading">Inspired Cliches</h2> + <p>Making unsolicited advice inspiring.</p> + <div class="social-icons flex"> + <a href="https://github.com/smiggiddy/odin-codeprojects" target="_blank"><i class="fa-brands fa-github"></i></a> + <a href="https://smig.tech" target="_blank"><i class="fa-solid fa-link"></i></a> + </div> + <p> © Copyright <%= new Date(Date.now()).getFullYear() %></p> + </footer> + <script src="/js/script.js"></script> + </body> +</html> diff --git a/messages/src/views/header.ejs b/messages/src/views/header.ejs new file mode 100644 index 0000000..bbb49ed --- /dev/null +++ b/messages/src/views/header.ejs @@ -0,0 +1,36 @@ +<!DOCTYPE html> +<html lang="en"> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <link rel="preconnect" href="https://fonts.googleapis.com"> + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link href="https://fonts.googleapis.com/css2?family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet"> + <link href="https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,100;0,300;0,400;0,700;0,900;1,100;1,300;1,400;1,700;1,900&family=Rokkitt:ital,wght@0,100..900;1,100..900&display=swap" rel="stylesheet"> + <title><%= pageTitle %></title> + <link href="/css/style.css" rel="stylesheet"> + <link rel="icon" href="/images/favicon.png"> + <!--<script src="https://kit.fontawesome.com/24f16b96cf.js" crossorigin="anonymous"></script>--> + <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/js/all.min.js"></script> + </head> + <body> + <nav> + <ul class="container flex"> + <li><a class="hover__underline" href="/">Home</a></li> + <% if (currentUser) { %> + <li class="nav__profile"><%= currentUser.username %> + <div class="hidden nav__hover"> + <ul class="container flex col"> + <li><a class="hover__underline" href="/profile?userId=<%= currentUser.user_id %>">view profile</a></li> + <li><a class="hover__underline" href="/new" class="new-link">new cliche</a></li> + <li><a class="hover__underline" href="/auth/logout">logout</a></li> + </ul> + </div> + </li> + <% } else { %> + <li><a class="hover__underline" href="/auth/login">Login</a></li> + <% }; %> + </ul> + </nav> + <div class="divider"></div> + diff --git a/messages/src/views/home.ejs b/messages/src/views/home.ejs new file mode 100644 index 0000000..efbc401 --- /dev/null +++ b/messages/src/views/home.ejs @@ -0,0 +1,49 @@ +<%- include('header') %> + <% if (currentUser) { %> + + <h1> Hi <%= currentUser.username %> </h1> + + <% } %> +<div class="container flex hero"> + + <div class="container hero"> + <h1 class="hero__heading">Unlock Your Potential, One cliche at a time.</h1> + <p class="hero__subheading">Read a cliche a day, to keep the brain at bay</p> + <a href="/auth/login" type="button" class="btn btn__link">Start Today</a> + </div> + +</div> +<section id="main"> + <div class="container flex"> + <div class="card"> + <h2>Lorem</h2> + <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p> + </div> + <div class="img"> + <p>This is an image</p> + </div> + + </div> + <div class="container flex"> + <div class="img"> + <p>This is an image</p> + </div> + <div class="card"> + <h2>Lorem</h2> + <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p> + </div> + + </div> + <div class="container flex"> + <div class="card"> + <h2>Lorem</h2> + <p>Lorem ipsum dolor sit amet consectetur, adipisicing elit. Nesciunt laboriosam, aliquam non ex consequuntur deleniti ut illo voluptatibus aperiam necessitatibus doloribus, quasi minima officia, inventore sed minus sit dolorem possimus.</p> + </div> + <div class="img"> + <p>This is an image</p> + </div> + + </div> +</section> + +<%- include('footer') %> diff --git a/messages/src/views/login.ejs b/messages/src/views/login.ejs new file mode 100644 index 0000000..265964f --- /dev/null +++ b/messages/src/views/login.ejs @@ -0,0 +1,30 @@ +<%- include('header') %> +<% if (locals.errors) {%> + <div class="center-horizontal"> + <ul> + <% errors.forEach(function(error) { %> + <li><%= error %></li> + <% }); %> + </ul> + </div> +<% }; %> +<main class="container full-page"> + <form class="center-horizontal" method="POST" action="/auth/login"> + <div class="form-item flex"> + <label for="username">Username</label> + <input type="text" name="username" required/> + </div> + <div class="form-item flex"> + <label for="password">Password</label> + <input type="password" name="password" required/> + </div> + <div class="form-item-row flex"> + <button type="submit" class="btn btn__form">Login</button> + </div> + <p>Need an account? + <a href="/auth/register">Click here!</a> + </p> + + </form> +</main> +<%- include('footer') %> diff --git a/messages/src/views/note-form.ejs b/messages/src/views/note-form.ejs new file mode 100644 index 0000000..b19f657 --- /dev/null +++ b/messages/src/views/note-form.ejs @@ -0,0 +1,19 @@ +<%- include("header") %> +<form action="/new" method="POST" class="center-horizontal"> + <div class="form-item flex"> + <label for="message">Message</label> + <textarea type="text" name="message" required></textarea> + </div> + <div class="form-item flex"> + <label for="media">Media</label> + <input type="text" name="media" /> + </div> + <div class="form-item flex col"> + <button type="submit" class="btn">Post</button> + <button type="button" class="btn" onClick="document.location.href='/'">Back</button> + </div> +</form> + + + +<%- include("footer") %> diff --git a/messages/src/views/register.ejs b/messages/src/views/register.ejs new file mode 100644 index 0000000..0919146 --- /dev/null +++ b/messages/src/views/register.ejs @@ -0,0 +1,35 @@ +<%- include('header') %> +<!-- views/partials/errors.ejs --> +<% if (locals.errors) {%> + <div class="center-horizontal"> + <ul> + <% errors.forEach(function(error) { %> + <li><%= error.msg %></li> + <% }); %> + </ul> + </div> +<% }; %> +<main class="container full-page"> + <form class="register-form center-horizontal" method="POST" action="/auth/register"> + <div class="form-item flex"> + <label for="username">username</label> + <input type="text" name="username" required/> + <div class="error"></div> + </div> + <div class="form-item flex"> + <label for="password">password</label> + <input type="password" name="password" required/> + <div class="error flex col"></div> + </div> + <div class="form-item flex"> + <label for="password-confirmation">confirm password</label> + <input type="password" name="password-confirmation" required/> + <div class="error"></div> + </div> + <div class="form-item flex"> + <button type="submit" class="btn btn__form">register</button> + </div> + + </form> +</main> +<%- include('footer') %> diff --git a/messages/src/views/view-profile.ejs b/messages/src/views/view-profile.ejs new file mode 100644 index 0000000..285154b --- /dev/null +++ b/messages/src/views/view-profile.ejs @@ -0,0 +1,20 @@ +<%- include("header") %> + +<div class="full-page container center-horizontal"> + <div class="container"> + <h1 class="username"><%= user.username %></h1> + <% if(currentUser.user_id === user.user_id ) { %> + <p>This is your profile page.</p> + <% }; %> + </div> + <div class="container"> + <h2><%= user.username %>'s <%= totalPosts %> post(s): </h2> + <% allPosts.forEach((post) => { %> + <div class="container"> + <p>"<%= post.message %>"</p> + </div> + <% }); %> + </div> + +</div> +<%- include("footer") %>